# Auto-generated examples
sources/pyside6/doc/examples/*
+# Auto-generated release notes
+sources/pyside6/doc/release_notes/*
+
distribute-*.egg
distribute-*.tar.gz
explore2
build_history/2*
*.qdocconf
-*.qdocconf.in
*.egg-info
*.pyc
"python.testing.unittestEnabled": true,
"flake8.args": [
"--config=.flake8"
- ]
+ ],
+ "grammarly.files.include": ["**/*.rst", "**/*.md"],
}
* `--ignore-git`, will skip the fetching and checkout steps for supermodule
and all submodules.
* `--limited-api=yes|no`, default yes if applicable. Set or clear the limited
- API flag. Ignored for Python 2.
+ API flag.
* `--module-subset`, allows for specifying the Qt modules to be built.
A minimal set is: `--module-subset=Core,Gui,Test,Widgets`.
* `--package-timestamp`, allows specifying the timestamp that will be used as
* QtCharts
* QtDataVisualization
* QtGraphs
+* QtGraphsWidgets
* QtMultimedia
* QtMultimediaWidgets
* QtNetworkAuth
* QtHttpServer
* QtLocation
* QtAsyncio
+* QtWebView
### Documentation and Bugs
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+from pathlib import Path
PYSIDE = 'pyside6'
PYSIDE_MODULE = 'PySide6'
"qmlls",
"qsb",
"balsam",
- "balsamui"]
+ "balsamui",
+ "svgtoqml",]
# tools that are bundled as .app in macOS, but are normal executables in Linux and Windows
PYSIDE_UNIX_BUNDLED_TOOLS = ["assistant",
# all Qt tools are in 'bin' folder in Windows
PYSIDE_WINDOWS_BIN_TOOLS = PYSIDE_UNIX_LIBEXEC_TOOLS + PYSIDE_LINUX_BIN_TOOLS
+
+PYSIDE_MULTIMEDIA_LIBS = ["avcodec", "avformat", "avutil",
+ "swresample", "swscale"]
+
+PYPROJECT_PATH = Path(__file__).parents[1] / "wheel_artifacts" / "pyproject.toml.base"
# Copyright (C) 2021 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import os
import platform
return py_library
-class BuildInfoCollectorMixin(object):
+class BuildInfoCollectorMixin:
build_base: str
build_lib: str
cmake: str
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+import os
import sys
-from .log import log, LogLevel
from pathlib import Path
+from typing import Any
-from . import PYSIDE, PYSIDE_MODULE, SHIBOKEN
-from .utils import available_pyside_tools
+from . import PYPROJECT_PATH, PYSIDE, PYSIDE_MODULE, SHIBOKEN
+from .log import LogLevel, log
+from .utils import available_pyside_tools, Singleton
+try:
+ import tomllib
+except ModuleNotFoundError:
+ import tomli as tomllib
-class Config(object):
+
+class Config(metaclass=Singleton):
def __init__(self):
# Constants
self._build_type_all = "all"
# The setup.py invocation type.
# top-level
# internal
- self.invocation_type = None
+ self.invocation_type: str = ""
# The type of the top-level build.
# all - build shiboken6 module, shiboken6-generator and PySide6
# shiboken6 - build only shiboken6 module
# shiboken6-generator - build only the shiboken6-generator
# pyside6 - build only PySide6 modules
- self.build_type = None
+ self.build_type: str = ""
# The internal build type, used for internal invocations of
# setup.py to build a specific module only.
- self.internal_build_type = None
+ self.internal_build_type: str = ""
# Options that can be given to --build-type and
# --internal-build-type
# Names to be passed to setuptools.setup() name key,
# so not package name, but rather project name as it appears
# in the wheel name and on PyPi.
- self.shiboken_module_st_name = SHIBOKEN
- self.shiboken_generator_st_name = f"{SHIBOKEN}-generator"
- self.pyside_st_name = PYSIDE_MODULE
+ self.shiboken_module_st_name: str = SHIBOKEN
+ self.shiboken_generator_st_name: str = f"{SHIBOKEN}-generator"
+ self.pyside_st_name: str = PYSIDE_MODULE
# Path to CMake toolchain file when intending to cross compile
# the project.
- self.cmake_toolchain_file = None
+ self.cmake_toolchain_file: str | os.PathLike = ""
# Store where host shiboken is built during a cross-build.
- self.shiboken_host_query_path = None
-
- # Used by check_allowed_python_version to validate the
- # interpreter version.
- self.python_version_classifiers = [
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.9',
- 'Programming Language :: Python :: 3.10',
- 'Programming Language :: Python :: 3.11',
- 'Programming Language :: Python :: 3.12',
- ]
+ self.shiboken_host_query_path: str = ""
+
+ self.setup_script_dir: str | os.PathLike = ""
+
+ # Getting data from base pyproject.toml file to be consistent
+
+ if not PYPROJECT_PATH.exists():
+ log.error("'pyproject.toml.base' not found in '{pyproject_path}'")
- self.setup_script_dir = None
+ with open(PYPROJECT_PATH, "rb") as f:
+ _pyproject_data = tomllib.load(f)["project"]
+
+ self.setup_kwargs: dict[str, Any] = {}
+ self.setup_kwargs['long_description_content_type'] = 'text/markdown'
+
+ self.setup_kwargs['keywords'] = _pyproject_data["keywords"]
+ _author, _email = _pyproject_data["authors"][0]
+ self.setup_kwargs['author'] = _author
+ self.setup_kwargs['author_email'] = _email
+ self.setup_kwargs['url'] = _pyproject_data["urls"]["Homepage"]
+ self.setup_kwargs['license'] = _pyproject_data["license"]["text"]
+ self.setup_kwargs['python_requires'] = _pyproject_data["requires-python"]
+
+ self.classifiers = _pyproject_data["classifiers"]
+ self.setup_kwargs['classifiers'] = self.classifiers
def init_config(self,
- build_type=None,
- internal_build_type=None,
+ build_type="",
+ internal_build_type="",
cmd_class_dict=None,
package_version=None,
ext_modules=None,
- setup_script_dir=None,
- cmake_toolchain_file=None,
+ setup_script_dir: str | os.PathLike = "",
+ cmake_toolchain_file: str | os.PathLike = "",
log_level=LogLevel.INFO,
- qt_install_path: Path = None):
+ qt_install_dir: str | os.PathLike = ""):
"""
Sets up the global singleton config which is used in many parts
of the setup process.
self.cmake_toolchain_file = cmake_toolchain_file
- setup_kwargs = {}
- setup_kwargs['long_description'] = self.get_long_description()
- setup_kwargs['long_description_content_type'] = 'text/markdown'
- setup_kwargs['keywords'] = 'Qt'
- setup_kwargs['author'] = 'Qt for Python Team'
- setup_kwargs['author_email'] = 'pyside@qt-project.org'
- setup_kwargs['url'] = 'https://www.pyside.org'
- setup_kwargs['download_url'] = 'https://download.qt.io/official_releases/QtForPython'
- setup_kwargs['license'] = 'LGPL'
- setup_kwargs['zip_safe'] = False
- setup_kwargs['cmdclass'] = cmd_class_dict
- setup_kwargs['version'] = package_version
- setup_kwargs['python_requires'] = ">=3.9, <3.13"
+ self.setup_kwargs['long_description'] = self.get_long_description()
+ self.setup_kwargs['cmdclass'] = cmd_class_dict
+ self.setup_kwargs['version'] = package_version
if log_level == LogLevel.QUIET:
# Tells setuptools to be quiet, and only print warnings or errors.
# Makes way less noise in the terminal when building.
- setup_kwargs['verbose'] = 0
+ self.setup_kwargs['verbose'] = 0
# Setting these two keys is still a bit of a discussion point.
# In general not setting them will allow using "build" and
# The only plausible usage of it, is if we will implement a
# correctly functioning setup.py develop command (or bdist_egg).
# But currently that doesn't seem to work.
- setup_kwargs['packages'] = self.get_setup_tools_packages_for_current_build()
- setup_kwargs['package_dir'] = self.get_package_name_to_dir_path_mapping()
+ self.setup_kwargs['packages'] = self.get_setup_tools_packages_for_current_build()
+ self.setup_kwargs['package_dir'] = self.get_package_name_to_dir_path_mapping()
# Add a bogus extension module (will never be built here since
# we are overriding the build command to do it using cmake) so
# things like bdist_egg will know that there are extension
# modules and will name the dist with the full platform info.
- setup_kwargs['ext_modules'] = ext_modules
-
- common_classifiers = [
- 'Development Status :: 5 - Production/Stable',
- 'Environment :: Console',
- 'Environment :: MacOS X',
- 'Environment :: X11 Applications :: Qt',
- 'Environment :: Win32 (MS Windows)',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
- 'License :: Other/Proprietary License',
- 'Operating System :: MacOS :: MacOS X',
- 'Operating System :: POSIX',
- 'Operating System :: POSIX :: Linux',
- 'Operating System :: Microsoft',
- 'Operating System :: Microsoft :: Windows',
- 'Programming Language :: C++']
- common_classifiers.extend(self.python_version_classifiers)
- common_classifiers.extend([
- 'Topic :: Database',
- 'Topic :: Software Development',
- 'Topic :: Software Development :: Code Generators',
- 'Topic :: Software Development :: Libraries :: Application Frameworks',
- 'Topic :: Software Development :: User Interfaces',
- 'Topic :: Software Development :: Widget Sets'])
- setup_kwargs['classifiers'] = common_classifiers
+ self.setup_kwargs['ext_modules'] = ext_modules
package_name = self.package_name()
if self.internal_build_type == self.shiboken_module_option_name:
- setup_kwargs['name'] = self.shiboken_module_st_name
- setup_kwargs['description'] = "Python / C++ bindings helper module"
- setup_kwargs['entry_points'] = {}
+ self.setup_kwargs['name'] = self.shiboken_module_st_name
+ self.setup_kwargs['description'] = "Python / C++ bindings helper module"
+ self.setup_kwargs['entry_points'] = {}
elif self.internal_build_type == self.shiboken_generator_option_name:
- setup_kwargs['name'] = self.shiboken_generator_st_name
- setup_kwargs['description'] = "Python / C++ bindings generator"
- setup_kwargs['install_requires'] = [
+ self.setup_kwargs['name'] = self.shiboken_generator_st_name
+ self.setup_kwargs['description'] = "Python / C++ bindings generator"
+ self.setup_kwargs['install_requires'] = [
f"{self.shiboken_module_st_name}=={package_version}"
]
- setup_kwargs['entry_points'] = {
+ self.setup_kwargs['entry_points'] = {
'console_scripts': [
f'{SHIBOKEN} = {package_name}.scripts.shiboken_tool:main',
f'{SHIBOKEN}-genpyi = {package_name}.scripts.shiboken_tool:genpyi',
}
elif self.internal_build_type == self.pyside_option_name:
- setup_kwargs['name'] = self.pyside_st_name
- setup_kwargs['description'] = ("Python bindings for the Qt cross-platform application "
- "and UI framework")
- setup_kwargs['install_requires'] = [
+ self.setup_kwargs['name'] = self.pyside_st_name
+ self.setup_kwargs['description'] = (
+ "Python bindings for the Qt cross-platform application and UI framework"
+ )
+ self.setup_kwargs['install_requires'] = [
f"{self.shiboken_module_st_name}=={package_version}"
]
- if qt_install_path:
- _pyside_tools = available_pyside_tools(qt_tools_path=qt_install_path)
+ if qt_install_dir:
+ _pyside_tools = available_pyside_tools(qt_tools_path=Path(qt_install_dir))
# replacing pyside6-android_deploy by pyside6-android-deploy for consistency
- # Also, the tool should not exist in any other platform than Linux
+ # Also, the tool should not exist in any other platform than Linux and macOS
_console_scripts = []
- if ("android_deploy" in _pyside_tools) and sys.platform.startswith("linux"):
+ if ("android_deploy" in _pyside_tools) and sys.platform in ["linux", "darwin"]:
_console_scripts = [(f"{PYSIDE}-android-deploy ="
" PySide6.scripts.pyside_tool:android_deploy")]
_pyside_tools.remove("android_deploy")
_console_scripts.extend([f'{PYSIDE}-{tool} = {package_name}.scripts.pyside_tool:'
f'{tool}' for tool in _pyside_tools])
- setup_kwargs['entry_points'] = {'console_scripts': _console_scripts}
-
- self.setup_kwargs = setup_kwargs
+ self.setup_kwargs['entry_points'] = {'console_scripts': _console_scripts}
def get_long_description(self):
readme_filename = 'README.md'
elif self.is_internal_pyside_build():
readme_filename = f'README.{PYSIDE}.md'
- content = ''
- changes = ''
- try:
- with open(self.setup_script_dir / readme_filename) as f:
- readme = f.read()
- except Exception as e:
- log.error(f"Couldn't read contents of {readme_filename}. {e}")
- raise
+ with open(Path(self.setup_script_dir) / readme_filename) as f:
+ readme = f.read()
# Don't include CHANGES.rst for now, because we have not decided
# how to handle change files yet.
include_changes = False
if include_changes:
try:
- with open(self.setup_script_dir / changes_filename) as f:
+ changes = ''
+ with open(Path(self.setup_script_dir) / changes_filename) as f:
changes = f.read()
except Exception as e:
log.error(f"Couldn't read contents of {changes_filename}. {e}")
raise
- content += readme
-
- if changes:
- content += f"\n\n{changes}"
+ return f"{readme}\n\n{changes}"
- return content
+ return readme
def package_name(self):
"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import logging
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import importlib
import os
import platform
import re
import sys
+import subprocess
import sysconfig
import time
from packaging.version import parse as parse_version
from pathlib import Path
from shutil import copytree, rmtree
-from textwrap import dedent
# PYSIDE-1760: Pre-load setuptools modules early to avoid racing conditions.
# may be touched (should be avoided anyway, btw.)
from .qtinfo import QtInfo
from .utils import (copydir, copyfile, detect_clang,
get_numpy_location, get_python_dict,
- linux_fix_rpaths_for_library, macos_fix_rpaths_for_library,
+ linux_fix_rpaths_for_library, macos_fix_rpaths_for_library, parse_modules,
platform_cmake_options, remove_tree, run_process,
run_process_output, update_env_path, which)
from . import PYSIDE, PYSIDE_MODULE, SHIBOKEN
pattern = r'Programming Language :: Python :: (\d+)\.(\d+)'
supported = []
- for line in config.python_version_classifiers:
+ for line in config.classifiers:
found = re.search(pattern, line)
if found:
major = int(found.group(1))
qt_src_dir = maybe_qt_src_dir
+def get_soname(clang_lib_path: Path) -> str:
+ """Getting SONAME from a shared library using readelf. Works only on Linux.
+ """
+ clang_lib_path = Path(clang_lib_path)
+ try:
+ result = subprocess.run(['readelf', '-d', str(clang_lib_path)],
+ capture_output=True, text=True, check=True)
+ for line in result.stdout.split('\n'):
+ if 'SONAME' in line:
+ soname = line.split('[')[1].split(']')[0]
+ return soname
+ except subprocess.CalledProcessError as e:
+ print(f"Failed to get SONAME: {e}")
+ return None
+
+
class PysideInstall(_install, CommandMixin):
user_options = _install.user_options + CommandMixin.mixin_user_options
for ext in config.get_buildable_extensions():
self.build_extension(ext)
- if OPTION["BUILDTESTS"]:
- # we record the latest successful build and note the
- # build directory for supporting the tests.
- timestamp = time.strftime('%Y-%m-%d_%H%M%S')
- build_history = setup_script_dir / 'build_history'
- unique_dir = build_history / timestamp
- unique_dir.mkdir(parents=True)
- fpath = unique_dir / 'build_dir.txt'
- with open(fpath, 'w') as f:
- print(self.build_dir, file=f)
- print(self.build_classifiers, file=f)
- log.info(f"Created {build_history}")
+ # We always record the history, whether tests are built or not.
+ # Record the latest successful build and note the
+ # build directory for supporting the tests or other tools.
+ timestamp = time.strftime('%Y-%m-%d_%H%M%S')
+ build_history = setup_script_dir / 'build_history'
+ unique_dir = build_history / timestamp
+ unique_dir.mkdir(parents=True)
+ fpath = unique_dir / 'build_dir.txt'
+ with open(fpath, 'w') as f:
+ print(self.build_dir, file=f)
+ print(self.build_classifiers, file=f)
+ log.info(f"Created {build_history}")
if not OPTION["SKIP_PACKAGING"]:
# Build patchelf if needed
except Exception as e:
log.warning(f'problem renaming "{self.st_build_dir}"')
log.warning(f'ignored error: {type(e).__name__}: {e}')
+
else:
log.info("Skipped preparing and building packages.")
log.info(f"--- Build completed ({elapsed()}s)")
log.info(f"Make generator: {self.make_generator}")
log.info(f"Make jobs: {OPTION['JOBS']}")
log.info("-" * 3)
- log.info(f"setup.py directory: {self.script_dir}")
- log.info(f"Build scripts directory: {build_scripts_dir}")
- log.info(f"Sources directory: {self.sources_dir}")
- log.info(dedent(f"""
- Building {config.package_name()} will create and touch directories
- in the following order:
- make build directory ->
- make install directory ->
- setuptools build directory ->
- setuptools install directory
- (usually path-installed-python/lib/python*/site-packages/*)
- """))
+ log.info(f"setup.py directory: {self.script_dir}")
+ log.info(f"Build scripts directory: {build_scripts_dir}")
+ log.info(f"Sources directory: {self.sources_dir}")
log.info(f"make build directory: {self.build_dir}")
log.info(f"make install directory: {self.install_dir}")
log.info(f"setuptools build directory: {self.st_build_dir}")
log.info(f"setuptools install directory: {setuptools_install_prefix}")
- log.info(dedent(f"""
- make-installed site-packages directory: {self.site_packages_dir}
- (only relevant for copying files from 'make install directory'
- to 'setuptools build directory'
- """))
log.info("-" * 3)
log.info(f"Python executable: {self.py_executable}")
log.info(f"Python includes: {self.py_include_dir}")
log.info("-" * 3)
if sys.platform == 'win32':
log.info(f"OpenSSL dll directory: {OPTION['OPENSSL']}")
- if sys.platform == 'darwin':
+ # for cross-compilation it is possible to use a macOS host, but
+ # pyside_macos_deployment_target is not relevant for the target.
+ # The only exception here is when we are trying to cross-compile from intel mac to m1 mac.
+ # This case is not supported yet.
+ if sys.platform == 'darwin' and not self.is_cross_compile:
pyside_macos_deployment_target = (macos_pyside_min_deployment_target())
log.info(f"MACOSX_DEPLOYMENT_TARGET set to: {pyside_macos_deployment_target}")
log.info("=" * 30)
cmake_cmd.append(f"-DCMAKE_UNITY_BUILD_BATCH_SIZE={batch_size}")
log.info("Using UNITY build")
+ if OPTION['SHIBOKEN_FORCE_PROCESS_SYSTEM_HEADERS']:
+ cmake_cmd.append("-DPYSIDE_TREAT_QT_INCLUDE_DIRS_AS_NON_SYSTEM=ON")
+ log.info("Shiboken will now process system Qt headers")
+
+ if OPTION['SHIBOKEN_EXTRA_INCLUDE_PATHS']:
+ extra_include_paths = ';'.join(OPTION['SHIBOKEN_EXTRA_INCLUDE_PATHS'].split(','))
+ cmake_cmd.append(f"-DSHIBOKEN_FORCE_PROCESS_SYSTEM_INCLUDE_PATHS={extra_include_paths}")
+ log.info(f"Shiboken will now process system headers from: {extra_include_paths}")
+
cmake_cmd += [
"-G", self.make_generator,
f"-DBUILD_TESTS={self.build_tests}",
f"Path given: {config_dir}")
if OPTION["MODULE_SUBSET"]:
- module_sub_set = ''
- for m in OPTION["MODULE_SUBSET"].split(','):
- if m.startswith('Qt'):
- m = m[2:]
- if module_sub_set:
- module_sub_set += ';'
- module_sub_set += m
- cmake_cmd.append(f"-DMODULES={module_sub_set}")
+ cmake_cmd.append(f"-DMODULES={parse_modules(OPTION['MODULE_SUBSET'])}")
if OPTION["SKIP_MODULES"]:
- skip_modules = ''
- for m in OPTION["SKIP_MODULES"].split(','):
- if m.startswith('Qt'):
- m = m[2:]
- if skip_modules:
- skip_modules += ';'
- skip_modules += m
- cmake_cmd.append(f"-DSKIP_MODULES={skip_modules}")
+ cmake_cmd.append(f"-DSKIP_MODULES={parse_modules(OPTION['SKIP_MODULES'])}")
+
# Add source location for generating documentation
cmake_src_dir = OPTION["QT_SRC"] if OPTION["QT_SRC"] else qt_src_dir
if cmake_src_dir:
if OPTION['NO_OVERRIDE_OPTIMIZATION_FLAGS']:
cmake_cmd.append("-DQFP_NO_OVERRIDE_OPTIMIZATION_FLAGS=1")
- if OPTION["LIMITED_API"] == "yes":
- cmake_cmd.append("-DFORCE_LIMITED_API=yes")
- elif OPTION["LIMITED_API"] == "no":
- cmake_cmd.append("-DFORCE_LIMITED_API=no")
- elif not OPTION["LIMITED_API"]:
+ if not OPTION["LIMITED_API"]:
if sys.platform == 'win32' and self.debug:
cmake_cmd.append("-DFORCE_LIMITED_API=no")
else:
- raise SetupError("option limited-api must be 'yes' or 'no' "
- "(default yes if applicable, i.e. Python "
- "version >= 3.9 and release build if on Windows)")
+ if OPTION["LIMITED_API"].lower() in ("yes", "y", "1", "true"):
+ cmake_cmd.append("-DFORCE_LIMITED_API=yes")
+ elif OPTION["LIMITED_API"].lower() in ("no", "n", "0", "false"):
+ cmake_cmd.append("-DFORCE_LIMITED_API=no")
+ else:
+ raise SetupError(
+ "Option '--limited-api' must be 'yes' or 'no'."
+ f"Default is yes if Python version >= {get_allowed_python_versions()[0]} "
+ "and Release build on Windows"
+ )
if OPTION["DISABLE_PYI"]:
cmake_cmd.append("-DDISABLE_PYI=yes")
+ if OPTION["UNOPTIMIZE"]:
+ value = OPTION["UNOPTIMIZE"]
+ cmake_cmd.append(f"-DSHIBOKEN_UNOPTIMIZE={value}")
+
if OPTION["LOG_LEVEL"] == LogLevel.VERBOSE:
cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON")
else:
cmake_cmd += platform_cmake_options()
- if sys.platform == 'darwin':
+ # for a macOS host, cross-compilation is possible, but for the host system as such
+ # we only build shiboken. Hence the following code can be skipped.
+ if sys.platform == 'darwin' and not self.is_cross_compile:
if OPTION["MACOS_ARCH"]:
# also tell cmake which architecture to use
cmake_cmd.append(f"-DCMAKE_OSX_ARCHITECTURES:STRING={OPTION['MACOS_ARCH']}")
# cross-compiling pyside.
cmake_cmd.append(f"-DQFP_SHIBOKEN_TARGET_PATH={self.install_dir}")
+ if OPTION["SKIP_MYPY_TEST"]:
+ cmake_cmd.append("-DSKIP_MYPY_TEST=1")
+
if self.cmake_toolchain_file:
cmake_cmd.append(f"-DCMAKE_TOOLCHAIN_FILE={self.cmake_toolchain_file}")
if run_process(cmd_make) != 0:
raise SetupError(f"Error compiling {extension}")
- if sys.version_info == (3, 6) and sys.platform == "darwin":
- # Python 3.6 has a Sphinx problem because of docutils 0.17 .
- # Instead of pinning v0.16, setting the default encoding fixes that.
- # Since other platforms are not affected, we restrict this to macOS.
- if "UTF-8" not in os.environ.get("LC_ALL", ""):
- os.environ["LC_ALL"] = "en_US.UTF-8"
-
if OPTION["BUILD_DOCS"]:
if extension.lower() == SHIBOKEN:
found = importlib.util.find_spec("sphinx")
if clang_lib_path.exists():
basename = clang_lib_path.name
+ # In case of static libclang we don't need the lib file inside the wheel
+ if '.a' == clang_lib_path.suffix:
+ log.info("Skip copying libclang archive to the package.")
+ return
+
log.info(f"Copying libclang shared library {clang_lib_path} to the package "
f"folder as {basename}.")
destination_path = destination_dir / basename
+ # It is possible that the resolved libclang has a different SONAME
+ # For example the actual libclang might be named libclang.so.14.0.0 and its
+ # SONAME might be libclang.so.13
+ # In this case, the ideal approach is to find the SONAME and create a symlink to the
+ # actual libclang in the destination directory. But, Python packaging (setuptools)
+ # does not support symlinks.
+ # So, we rename the actual libclang to the SONAME and copy it to the destination
+ if sys.platform == 'linux':
+ soname = get_soname(clang_lib_path)
+ if soname and soname != clang_lib_path.name:
+ destination_path = destination_path.parent / soname
+
# Need to modify permissions in case file is not writable
# (a reinstall would cause a permission denied error).
copyfile(clang_lib_path,
user_options = CommandMixin.mixin_user_options
def __init__(self, *args, **kwargs):
- if args[0].commands[0] == "build_rst_docs":
- args[0].commands[0] = "build_base_docs"
- log.warning("'build_rst_docs' is deprecated and will be removed. "
- "Please use 'build_base_docs' instead.")
self.command_name = "build_base_docs"
Command.__init__(self, *args, **kwargs)
CommandMixin.__init__(self)
raise SetupError(f"Error running CMake for {self.doc_dir}")
if self.name == PYSIDE:
+ def run_script(script_path, args=None):
+ cmd = [sys.executable, os.fspath(script_path)]
+ if args:
+ cmd.extend(args)
+ if run_process(cmd) != 0:
+ raise SetupError(f"Error running {script_path}")
+
self.sphinx_src = self.out_dir / "base"
+ # Generates the .rst files from the examples
example_gallery = config.setup_script_dir / "tools" / "example_gallery" / "main.py"
- assert example_gallery.is_file()
- example_gallery_cmd = [sys.executable, os.fspath(example_gallery)]
- if OPTION["LOG_LEVEL"] == LogLevel.QUIET:
- example_gallery_cmd.append("--quiet")
- qt_src_dir = OPTION['QT_SRC']
- if qt_src_dir:
- example_gallery_cmd.extend(["--qt-src-dir", qt_src_dir])
- if run_process(example_gallery_cmd) != 0:
- raise SetupError(f"Error running example gallery for {self.doc_dir}")
+ if example_gallery.is_file():
+ example_gallery_args = []
+ if OPTION["LOG_LEVEL"] == LogLevel.QUIET:
+ example_gallery_args.append("--quiet")
+ qt_src_dir = OPTION['QT_SRC']
+ if qt_src_dir:
+ example_gallery_args.extend(["--qt-src-dir", qt_src_dir])
+ run_script(example_gallery, example_gallery_args)
+ else:
+ log.warning("Example gallery script for generating .rst for examples"
+ f"not found: {example_gallery}")
+
+ # Generates the .rst files from the release notes
+ release_notes = config.setup_script_dir / "tools" / "release_notes" / "main.py"
+ if release_notes.is_file():
+ release_notes_args = []
+ if OPTION["LOG_LEVEL"] != LogLevel.QUIET:
+ release_notes_args.append("--verbose")
+ run_script(release_notes, release_notes_args)
+ else:
+ log.warning("Release notes script for generating .rst for release notes"
+ f"not found: {release_notes}")
elif self.name == SHIBOKEN:
self.sphinx_src = self.out_dir
- sphinx_cmd = ["sphinx-build", "-b", "html", "-j", "auto", "-c",
+ sphinx_cmd = ["sphinx-build", "-b", "html", "-j", "auto", "-n", "-c",
str(self.sphinx_src), str(self.doc_dir),
str(self.out_dir)]
if run_process(sphinx_cmd) != 0:
'install': PysideInstall,
'install_lib': PysideInstallLib,
'build_base_docs': PysideBaseDocs,
- # TODO: Remove build_rst_docs in the next version, see PYSIDE-2504
- 'build_rst_docs': PysideBaseDocs,
}
if wheel_module_exists:
pyside_bdist_wheel = get_bdist_wheel_override()
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
from setuptools import Command
from .log import log, LogLevel
from .qtinfo import QtInfo
-from .utils import memoize, which
+from .utils import memoize, which, Singleton
_AVAILABLE_MKSPECS = ["ninja", "msvc", "mingw"] if sys.platform == "win32" else ["ninja", "make"]
--shiboken-host-path Path to host shiboken package when cross-compiling
--qt-host-path Path to host Qt installation when cross-compiling
--disable-pyi Disable .pyi file generation
+ --skip-mypy-test Skip the mypy test which can take long in debug builds
"""
log.warning(w)
-class Options(object):
+class Options(metaclass=Singleton):
def __init__(self):
# Dictionary containing values of all the possible options.
self.dict[name] = value
return value
+ def find_qtpaths(self):
+ # Skip the first run that will trigger the three different build
+ # stated of the setup process
+ if self.dict["internal-build-type"] is None:
+ return None
+ # for these command --qtpaths should not be required
+ no_qtpaths_commands = ["--help", "--help-commands", "--qt-target-path", "build_base_docs"]
-options = Options()
-
-
-def has_option(*args, **kwargs):
- return options.has_option(*args, **kwargs)
-
-
-def option_value(*args, **kwargs):
- return options.option_value(*args, **kwargs)
-
+ for no_qtpaths_command in no_qtpaths_commands:
+ if any(no_qtpaths_command in argument for argument in sys.argv):
+ return None
-def _jobs_option_value():
- """Option value for parallel builds."""
- value = option_value('parallel', short_option_name='j')
- if value:
- return f"-j{value}" if not value.startswith('-j') else value
- return ''
+ qtpaths = self.option_value("qtpaths")
+ if qtpaths is not None:
+ return qtpaths
+ # if qtpaths is not given as cli option, try to find it in PATH
+ qtpaths = which("qtpaths6")
+ if qtpaths is not None:
+ return str(Path(qtpaths).resolve())
-def find_qtpaths():
- # for these command --qtpaths should not be required
- no_qtpaths_commands = ["--help", "--help-commands", "--qt-target-path", "build_base_docs",
- "build_rst_docs"]
+ qtpaths = which("qtpaths")
+ if qtpaths is not None:
+ return str(Path(qtpaths).resolve())
- for no_qtpaths_command in no_qtpaths_commands:
- if any(no_qtpaths_command in argument for argument in sys.argv):
- return None
+ if qtpaths is None:
+ sys.exit(-1)
- qtpaths = option_value("qtpaths")
- if qtpaths:
return qtpaths
- # if qtpaths is not given as cli option, try to find it in PATH
- qtpaths = which("qtpaths6")
- if qtpaths:
- return str(qtpaths.resolve())
-
- qtpaths = which("qtpaths")
- if qtpaths:
- return str(qtpaths.resolve())
-
- return qtpaths
-
-
-# Declare options which need to be known when instantiating the setuptools
-# commands or even earlier during SetupRunner.run().
-OPTION = {
- "BUILD_TYPE": option_value("build-type"),
- "INTERNAL_BUILD_TYPE": option_value("internal-build-type"),
- # number of parallel build jobs
- "JOBS": _jobs_option_value(),
- # Legacy, not used any more.
- "JOM": has_option('jom'),
- "MACOS_USE_LIBCPP": has_option("macos-use-libc++"),
- "LOG_LEVEL": option_value("log-level", remove=False),
- "QUIET": has_option('quiet'),
- "VERBOSE_BUILD": has_option('verbose-build'),
- "SNAPSHOT_BUILD": has_option("snapshot-build"),
- "LIMITED_API": option_value("limited-api"),
- "DISABLE_PYI": has_option("disable-pyi"),
- "PACKAGE_TIMESTAMP": option_value("package-timestamp"),
- # This is used automatically by setuptools.command.install object, to
- # specify the final installation location.
- "FINAL_INSTALL_PREFIX": option_value("prefix", remove=False),
- "CMAKE_TOOLCHAIN_FILE": option_value("cmake-toolchain-file"),
- "SHIBOKEN_HOST_PATH": option_value("shiboken-host-path"),
- "SHIBOKEN_HOST_PATH_QUERY_FILE": option_value("internal-shiboken-host-path-query-file"),
- "QT_HOST_PATH": option_value("qt-host-path"),
- # This is used to identify the template for doc builds
- "QTPATHS": find_qtpaths()
- # This is an optional command line option. If --qtpaths is not provided via command-line,
- # then qtpaths is checked inside PATH variable
-}
-
-_deprecated_option_jobs = option_value('jobs')
-if _deprecated_option_jobs:
- _warn_deprecated_option('jobs', 'parallel')
- OPTION["JOBS"] = _deprecated_option_jobs
-
-
-class CommandMixin(object):
+ def _jobs_option_value(self):
+ """Option value for parallel builds."""
+ value = self.option_value('parallel', short_option_name='j')
+
+ _deprecated_option_jobs = self.option_value('jobs')
+ if _deprecated_option_jobs:
+ _warn_deprecated_option('jobs', 'parallel')
+ value = _deprecated_option_jobs
+
+ if value:
+ return f"-j{value}" if not value.startswith('-j') else value
+ return ''
+
+ def resolve(self):
+ return {
+ "BUILD_TYPE": self.option_value("build-type"),
+ "INTERNAL_BUILD_TYPE": self.option_value("internal-build-type"),
+ # number of parallel build jobs
+ "JOBS": self._jobs_option_value(),
+ # Legacy, not used any more.
+ "JOM": self.has_option('jom'),
+ "MACOS_USE_LIBCPP": self.has_option("macos-use-libc++"),
+ "LOG_LEVEL": self.option_value("log-level", remove=False),
+ "QUIET": self.has_option('quiet'),
+ "VERBOSE_BUILD": self.has_option('verbose-build'),
+ "SNAPSHOT_BUILD": self.has_option("snapshot-build"),
+ "LIMITED_API": self.option_value("limited-api"),
+ "UNOPTIMIZE": self.option_value("unoptimize"),
+ "DISABLE_PYI": self.has_option("disable-pyi"),
+ "SKIP_MYPY_TEST": self.has_option("skip-mypy-test"),
+ "PACKAGE_TIMESTAMP": self.option_value("package-timestamp"),
+ # This is used automatically by setuptools.command.install object, to
+ # specify the final installation location.
+ "FINAL_INSTALL_PREFIX": self.option_value("prefix", remove=False),
+ "CMAKE_TOOLCHAIN_FILE": self.option_value("cmake-toolchain-file"),
+ "SHIBOKEN_HOST_PATH": self.option_value("shiboken-host-path"),
+ "SHIBOKEN_HOST_PATH_QUERY_FILE": self.option_value(
+ "internal-shiboken-host-path-query-file"
+ ),
+ "QT_HOST_PATH": self.option_value("qt-host-path"),
+ # This is used to identify the template for doc builds
+ "QTPATHS": self.find_qtpaths()
+ # This is an optional command line option.
+ # If --qtpaths is not provided via command-line,
+ # then qtpaths is checked inside PATH variable
+ }
+
+
+class CommandMixin:
"""Mixin for the setuptools build/install commands handling the options."""
_static_class_finalized_once = False
('plat-name=', None, 'The platform name for which we are cross-compiling'),
('unity', None, 'Use CMake UNITY_BUILD_MODE (obsolete)'),
('no-unity', None, 'Disable CMake UNITY_BUILD_MODE'),
- ('unity-build-batch-size=', None, 'Value of CMAKE_UNITY_BUILD_BATCH_SIZE')
+ ('unity-build-batch-size=', None, 'Value of CMAKE_UNITY_BUILD_BATCH_SIZE'),
+ # shiboken-force-process-system-headers option is specifically used to tell the clang
+ # inside shiboken to process the system headers, when building against a system Qt.
+ #
+ # This option is specific for Flatpak and OS distro builds of PySide6. So, use with
+ # caution as it may also try to parse other global headers.
+ ('shiboken-force-process-system-headers', None,
+ 'When building PySide against system Qt, shiboken does not ignore the system Qt headers'),
+ # shiboken-extra-inlude-paths option is specifically used to tell the clang inside shiboken
+ # to include extra paths when parsing the headers. Use with caution.
+ ('shiboken-extra-include-paths=', None,
+ 'Extra include paths for shiboken. Comma separated.'),
+ # flatpak option is used to build PySide6 for Flatpak. Flatpak is a special case where
+ # some of the headers for the Qt modules are located as system headers in /usr/include in
+ # the KDE flatpak SDK. Therefore --shiboken-force-process-system headers will be by
+ # default enabled when --flatpak is enabled.
+ # Apart from that, headers for certain Qt modules like QtWebEngine, QtPdf etc. are located
+ # in /app/include from the Flapak WebEngine baseapp. Therefore when the --flatpak option is
+ # enabled, the extra include path of /app/include will be added to the option
+ # --shiboken-extra-include-paths.
+ ('flatpak', None, 'Build PySide6 for Flatpak.'),
]
def __init__(self):
self.shiboken_target_path = None
self.python_target_path = None
self.is_cross_compile = False
- self.cmake_toolchain_file = None
+ self.cmake_toolchain_file: str = ""
self.make_spec = None
self.macos_arch = None
self.macos_sysroot = None
self.unity = False
self.no_unity = False
self.unity_build_batch_size = "16"
+ self.shiboken_force_process_system_headers = False
+ self.shiboken_extra_include_paths = None
+ self.flatpak = False
# When initializing a command other than the main one (so the
# first one), we need to copy the user options from the main
# because we DON'T want those to be found when cross compiling.
# Currently when cross compiling, qt-target-path MUST be used.
using_cmake_toolchain_file = False
- cmake_toolchain_file = None
+ cmake_toolchain_file: str = ""
if OPTION["CMAKE_TOOLCHAIN_FILE"]:
self.is_cross_compile = True
using_cmake_toolchain_file = True
"Unity build mode is now the default.")
OPTION['UNITY'] = not self.no_unity
OPTION['UNITY_BUILD_BATCH_SIZE'] = self.unity_build_batch_size
+ OPTION['SHIBOKEN_FORCE_PROCESS_SYSTEM_HEADERS'] = self.shiboken_force_process_system_headers
+ OPTION['SHIBOKEN_EXTRA_INCLUDE_PATHS'] = self.shiboken_extra_include_paths
+ OPTION['FLATPAK'] = self.flatpak
+ if OPTION['FLATPAK']:
+ OPTION['SHIBOKEN_FORCE_PROCESS_SYSTEM_HEADERS'] = True
+ OPTION['SHIBOKEN_EXTRA_INCLUDE_PATHS'] = '/app/include'
qtpaths_abs_path = None
if self.qtpaths and Path(self.qtpaths).exists():
qt_target_path=qt_target_path,
cmake_toolchain_file=cmake_toolchain_file)
- if 'build_base_docs' not in sys.argv and 'build_rst_docs' not in sys.argv:
+ if 'build_base_docs' not in sys.argv:
try:
QtInfo().prefix_dir
except Exception as e:
if not self.qt_target_path:
log.error(
- "\nCould not find Qt. You can pass the --qt-target-path=<qt-dir> option "
- "as a hint where to find Qt. Error was:\n\n\n")
+ "Could not find Qt. You can pass the --qt-target-path=<qt-dir> option "
+ "as a hint where to find Qt.\n")
else:
- log.error(
- f"\nCould not find Qt via provided option --qt-target-path={qt_target_path}"
- "Error was:\n\n\n")
+ log.error("Could not find Qt via provided option "
+ f"--qt-target-path={qt_target_path}\n")
raise e
OPTION['CMAKE'] = self.cmake.resolve()
# while cross-compiling.
# Skip this process for the 'build_base_docs' command
if (not self.is_cross_compile
- and not self.qt_target_path
- and 'build_base_docs' not in sys.argv
- and 'build_rst_docs' not in sys.argv):
+ and not self.qt_target_path
+ and 'build_base_docs' not in sys.argv):
# Enforce usage of qmake in QtInfo if it was given explicitly.
if self.qmake:
self.has_qmake_option = True
return False
return True
+
+
+# OPTION dictionary that will be imported in other build_scripts
+OPTION = Options().resolve()
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
from pathlib import Path
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import fnmatch
from pathlib import Path
macos_add_rpath(rpath, binary)
-def prepare_standalone_package_macos(pyside_build, _vars):
+def prepare_standalone_package_macos(pyside_build, _vars, is_android=False):
built_modules = _vars['built_modules']
constrain_modules = None
ignored_modules = []
if not pyside_build.is_webengine_built(built_modules):
ignored_modules.extend(['libQt6WebEngine*.dylib'])
+
accepted_modules = ['libQt6*.6.dylib']
+ if is_android:
+ accepted_modules = ['libQt6*.so', '*-android-dependencies.xml']
+
if constrain_modules:
accepted_modules = [f"libQt6{module}*.6.dylib" for module in constrain_modules]
# <qt>/plugins/* -> <setup>/{st_package_name}/Qt/plugins
plugins_target = destination_qt_dir / "plugins"
filters = ["*.dylib"]
+ if is_android:
+ filters = ["*.so"]
copydir("{qt_plugins_dir}", plugins_target,
_filter=filters,
recursive=True,
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import sys
from pathlib import Path
from .. import PYSIDE, SHIBOKEN
from .linux import prepare_standalone_package_linux
from .macos import prepare_standalone_package_macos
-from .. import PYSIDE_UNIX_BIN_TOOLS, PYSIDE_UNIX_LIBEXEC_TOOLS, PYSIDE_UNIX_BUNDLED_TOOLS
+from .. import (PYSIDE_UNIX_BIN_TOOLS, PYSIDE_UNIX_LIBEXEC_TOOLS, PYSIDE_UNIX_BUNDLED_TOOLS,
+ PYSIDE_MULTIMEDIA_LIBS)
def _macos_copy_gui_executable(name, _vars=None):
scripts = ["pyside_tool.py", "metaobjectdump.py", "project.py", "qml.py",
"qtpy2cpp.py", "deploy.py"]
- script_dirs = ["qtpy2cpp_lib", "deploy_lib", "project"]
+ script_dirs = ["qtpy2cpp_lib", "deploy_lib", "project_lib"]
- if sys.platform.startswith("linux"):
+ if sys.platform in ["linux", "darwin"]:
scripts.append("android_deploy.py")
scripts.append("requirements-android.txt")
script_dirs.extend(["deploy_lib/android",
# <install>/share/{st_package_name}/glue/* ->
# <setup>/{st_package_name}/glue
- copydir(
- "{install_dir}/share/{st_package_name}/glue",
- "{st_build_dir}/{st_package_name}/glue",
- _vars=_vars)
+ for glue in ['glue', 'doc']:
+ source = "{install_dir}/share/{st_package_name}/" + glue
+ target = "{st_build_dir}/{st_package_name}/" + glue
+ copydir(source, target, _vars=_vars)
if not is_android:
# <source>/pyside6/{st_package_name}/support/* ->
"{st_build_dir}/{st_package_name}/jar",
_vars=_vars)
+ # Some libraries specific to Linux/Android from 6.8
+ # eg: the libav* libraries are required for the multimedia module
+ if config.is_internal_pyside_build() and sys.platform != "darwin":
+ qt_multimedia_filters = [f"lib{lib}*.so*" for lib in PYSIDE_MULTIMEDIA_LIBS]
+ copydir("{qt_lib_dir}", destination_qt_dir / "lib",
+ _filter=qt_multimedia_filters,
+ recursive=False, _vars=_vars, force_copy_symlinks=True)
+
# Copy Qt libs to package
if OPTION["STANDALONE"]:
if config.is_internal_pyside_build() or config.is_internal_shiboken_generator_build():
_vars['built_modules'] = generated_config['built_modules']
if sys.platform == 'darwin':
- prepare_standalone_package_macos(pyside_build, _vars)
+ prepare_standalone_package_macos(pyside_build, _vars, is_android=is_android)
else:
prepare_standalone_package_linux(pyside_build, _vars, cross_build,
is_android=is_android)
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import functools
import os
from ..config import config
from ..options import OPTION
from ..utils import (copydir, copyfile, copy_qt_metatypes,
- download_and_extract_7z, filter_match, makefile)
-from .. import PYSIDE, SHIBOKEN, PYSIDE_WINDOWS_BIN_TOOLS
+ download_and_extract_7z, filter_match, makefile, in_coin)
+from .. import PYSIDE, SHIBOKEN, PYSIDE_WINDOWS_BIN_TOOLS, PYSIDE_MULTIMEDIA_LIBS
def prepare_packages_win32(pyside_build, _vars):
target = f"{{st_build_dir}}/{{st_package_name}}/scripts/{script}"
copyfile(src, target, force=False, _vars=_vars)
- for script_dir in ("qtpy2cpp_lib", "deploy_lib", "project"):
+ for script_dir in ("qtpy2cpp_lib", "deploy_lib", "project_lib"):
src = f"{{install_dir}}/bin/{script_dir}"
target = f"{{st_build_dir}}/{{st_package_name}}/scripts/{script_dir}"
# Exclude subdirectory tests
# <install>/share/{st_package_name}/glue/* ->
# <setup>/{st_package_name}/glue
- copydir(
- "{install_dir}/share/{st_package_name}/glue",
- destination_dir / "glue",
- _vars=_vars)
+ for glue in ['glue', 'doc']:
+ source = "{install_dir}/share/{st_package_name}/" + glue
+ copydir(source, destination_dir / glue, _vars=_vars)
# <source>/pyside6/{st_package_name}/support/* ->
# <setup>/{st_package_name}/support/*
def copy_msvc_redist_files(destination_dir):
- in_coin = os.environ.get('COIN_LAUNCH_PARAMETERS', None)
- if in_coin is None:
+ if not in_coin():
log.info("Qt dependency DLLs (MSVC redist) will not be copied.")
return
raise FileNotFoundError(msg)
-def copy_qt_dependency_dlls(_vars, destination_qt_dir, artifacts):
+def download_qt_dependency_dlls(_vars, destination_qt_dir, artifacts):
# Extract Qt dependency dlls when building on Qt CI.
- in_coin = os.environ.get('COIN_LAUNCH_PARAMETERS', None)
- if in_coin is None:
+ if not in_coin():
log.info("Qt dependency DLLs will not be downloaded and extracted.")
return
with tempfile.TemporaryDirectory() as temp_path:
redist_url = "https://download.qt.io/development_releases/prebuilt/vcredist/"
- zip_file = "pyside_qt_deps_64_2019.7z"
- if "{target_arch}".format(**_vars) == "32":
- zip_file = "pyside_qt_deps_32_2019.7z"
+ zip_file = "pyside_qt_deps_681_64_2022.7z"
try:
download_and_extract_7z(redist_url + zip_file, temp_path)
except Exception as e:
copy_translations = False
copy_qt_conf = False
copy_qt_permanent_artifacts = False
- copy_clang = True
# <qt>/bin/*.dll and Qt *.exe -> <setup>/{st_package_name}
- qt_artifacts_permanent = [
- "avcodec-60.dll",
- "avformat-60.dll",
- "avutil-58.dll",
- "swresample-4.dll",
- "swscale-7.dll",
- "opengl*.dll",
- "designer.exe",
- "linguist.exe",
- "lrelease.exe",
- "lupdate.exe",
- "lconvert.exe",
- "qtdiag.exe"
- ]
-
- # Choose which EGL library variants to copy.
- qt_artifacts_egl = [
- "libEGL{}.dll",
- "libGLESv2{}.dll"
- ]
- if pyside_build.qtinfo.build_type != 'debug_and_release':
- egl_suffix = '*'
- elif pyside_build.debug:
- egl_suffix = 'd'
- else:
- egl_suffix = ''
- qt_artifacts_egl = [a.format(egl_suffix) for a in qt_artifacts_egl]
+ qt_multimedia_artifacts_permanent = [f"{lib}-*.dll" for lib in PYSIDE_MULTIMEDIA_LIBS]
+
+ def qt_rhi_artifacts_permanent():
+ result = ["opengl*.dll"]
+ if pyside_build.qtinfo.build_type != 'debug_and_release':
+ suffix = '*'
+ elif pyside_build.debug:
+ suffix = 'd'
+ else:
+ suffix = ''
+ result.append(f"libEGL{suffix}.dll") # Needed in Qt 6 (ANGLE)?
+ result.append(f"libGLESv2{suffix}.dll")
+ return result
if copy_qt_permanent_artifacts:
- artifacts = qt_artifacts_permanent + qt_artifacts_egl
- copy_qt_dependency_dlls(_vars, destination_qt_dir, artifacts)
+ download_qt_dependency_dlls(_vars, destination_qt_dir, qt_rhi_artifacts_permanent())
+ if not copydir("{qt_bin_dir}", destination_qt_dir,
+ _filter=qt_multimedia_artifacts_permanent,
+ recursive=False, _vars=_vars):
+ msg = "The multimedia artifacts were not found."
+ log.warning(msg)
+ if in_coin(): # Allow local developer builds without -DQT_DEPLOY_FFMPEG...
+ raise FileNotFoundError(msg)
# <qt>/bin/*.dll and Qt *.pdbs -> <setup>/{st_package_name} part two
# File filter to copy only debug or only release files.
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import datetime
import os
from argparse import ArgumentParser, RawTextHelpFormatter
from enum import Enum, auto
from pathlib import Path
-from typing import List
DESC = """
Utility script for working with Qt for Python.
return None
-def command_log_string(args: List[str], directory: Path):
+def command_log_string(args: list[str], directory: Path):
result = f'[{directory.name}]'
for arg in args:
result += f' "{arg}"' if ' ' in arg else f' {arg}'
return result
-def execute(args: List[str]):
+def execute(args: list[str]):
"""Execute a command and print to log"""
log_string = command_log_string(args, Path.cwd())
print(log_string)
"""
Config file handling, cache and read function
"""
-config_dict = {}
+config_dict: dict = {}
def read_config_file(file_name):
def get_config_file(base_name) -> Path:
global user
- home = os.getenv('HOME')
+ home = os.getenv('HOME', default="")
if IS_WINDOWS:
# Set a HOME variable on Windows such that scp. etc.
# feel at home (locating .ssh).
if not home:
- home = os.getenv('HOMEDRIVE') + os.getenv('HOMEPATH')
+ home = os.getenv('HOMEDRIVE', default="") + os.getenv('HOMEPATH', default="")
os.environ['HOME'] = home
user = os.getenv('USERNAME')
- config_file = Path(os.getenv('APPDATA')) / base_name
+ config_file = Path(os.getenv('APPDATA', default="")) / base_name
else:
user = os.getenv('USER')
config_dir = Path(home) / '.config'
acceleration = read_acceleration_config()
if not IS_WINDOWS and acceleration == Acceleration.INCREDIBUILD:
arguments.append(INCREDIBUILD_CONSOLE)
- arguments.appendh('--avoid') # caching, v0.96.74
+ arguments.append('--avoid') # caching, v0.96.74
arguments.extend([read_config_python_binary(), 'setup.py', target])
build_arguments = read_config_build_arguments()
if opt_verbose and LOG_LEVEL_OPTION in build_arguments:
"""Run tests redirected into a log file with a time stamp"""
logfile_name = datetime.datetime.today().strftime("test_%Y%m%d_%H%M.txt")
binary = sys.executable
- command = f'"{binary}" testrunner.py test > {logfile_name}'
+ command = f'"{binary}" testrunner.py test --reruns 2 > {logfile_name}'
print(command_log_string([command], Path.cwd()))
start_time = time.time()
result = 0 if opt_dry_run else os.system(command)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import os
import subprocess
platform_cmake_options)
-class QtInfo(object):
+class QtInfo:
_instance = None # singleton helpers
def __new__(cls): # __new__ always a classmethod
self._force_qmake = False
self._use_cmake = False
self._qt_target_path = None
- self._cmake_toolchain_file = None
+ self._cmake_toolchain_file: str = ""
# Dict to cache qmake values.
self._query_dict = {}
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import os
import sys
from build_scripts.log import log, LogLevel
-class SetupRunner(object):
+class SetupRunner:
def __init__(self, orig_argv):
self.invocations_list = []
setup_cmd.append(self.construct_cmd_line_argument(name, value))
# Add --reuse-build option if requested and not already present.
- if (reuse_build and command in ('bdist_wheel', 'build', 'build_base_docs', 'build_rst_docs', 'install')
+ if (reuse_build and command in ('bdist_wheel', 'build', 'build_base_docs', 'install')
and not self.cmd_line_argument_is_in_args("reuse-build", modified_argv)):
setup_cmd.append(self.construct_cmd_line_argument("reuse-build"))
return setup_cmd
setup_script_dir=self.setup_script_dir,
cmake_toolchain_file=OPTION["CMAKE_TOOLCHAIN_FILE"],
log_level=OPTION["LOG_LEVEL"],
- qt_install_path=qt_install_path)
+ qt_install_dir=qt_install_path)
# Enable logging for both the top-level invocation of setup.py
# as well as for child invocations. We we now use
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import errno
import fnmatch
import subprocess
import sys
import tempfile
-import urllib.request as urllib
+import urllib.request
+import urllib.error
from collections import defaultdict
from pathlib import Path
from textwrap import dedent, indent
WindowsError = None
+class Singleton(type):
+ _instances = {}
+
+ def __call__(cls, *args, **kwargs):
+ if cls not in cls._instances:
+ cls._instances[cls] = super().__call__(*args, **kwargs)
+ return cls._instances[cls]
+
+
def which(name):
"""
Like shutil.which, but accepts a string or a PathLike and returns a Path
if isinstance(name, Path):
name = str(name)
path = shutil.which(name)
- if path is None:
- raise TypeError("None was returned")
- path = Path(path)
+ if path is not None:
+ path = Path(path)
except TypeError as e:
log.error(f"{name} was not found in PATH: {e}")
return path
if 'site-' in p:
numpy = Path(p).resolve() / 'numpy'
if numpy.is_dir():
- return os.fspath(numpy / 'core' / 'include')
+ candidate = numpy / '_core' / 'include' # Version 2
+ if not candidate.is_dir():
+ candidate = numpy / 'core' / 'include' # Version 1
+ if candidate.is_dir():
+ return os.fspath(candidate)
+ log.warning(f"Cannot find numpy include dir under {numpy}")
return None
# We use 'strict=False' to mimic os.path.realpath in case
# the directory doesn't exist.
+ # TODO: This code can probably be removed when 'python setup.py install' usage is removed.
link_target_path = src.resolve(strict=False)
if link_target_path.parent == src.parent:
link_target = Path(link_target_path.name)
os.chdir(target_dir)
if link_name.exists():
if (link_name.is_symlink()
- and os.readlink(link_name) == link_target):
+ and os.readlink(link_name) == str(link_target)):
log.info(f"Symlink already exists\n {link_name} ->\n {link_target}")
return dst
os.remove(link_name)
for i in range(1, 10):
try:
log.info(f"Downloading fileUrl {fileurl}, attempt #{i}")
- localfile, info = urllib.urlretrieve(fileurl)
+ localfile, info = urllib.request.urlretrieve(fileurl)
break
- except urllib.URLError:
+ except urllib.error.URLError:
pass
if not localfile:
log.error(f"Error downloading {fileurl} : {info}")
def linux_set_rpaths(patchelf, executable_path, rpath_string):
""" Patches the `executable_path` with a new rpath string. """
+ path = Path(executable_path)
+ mode = path.stat().st_mode
+ if (mode & stat.S_IWUSR) == 0:
+ log.info(f"patchelf: {executable_path} is read-only, making writeable.")
+ path.chmod(mode | stat.S_IWUSR)
+
cmd = [str(patchelf), '--set-rpath', str(rpath_string), str(executable_path)]
if run_process(cmd) != 0:
raise
-def get_qtci_virtualEnv(python_ver, host, hostArch, targetArch):
- _pExe = "python"
- _env = f"{os.environ.get('PYSIDE_VIRTUALENV') or 'env'+python_ver}"
- env_python = f"{_env}/bin/python"
- env_pip = f"{_env}/bin/pip"
-
- if host == "Windows":
- log.info("New virtualenv to build {targetArch} in {hostArch} host")
- _pExe = "python.exe"
- # With windows we are creating building 32-bit target in 64-bit host
- if hostArch == "X86_64" and targetArch == "X86":
- if python_ver.startswith("3"):
- var = f"PYTHON{python_ver}-32_PATH"
- log.info(f"Try to find python from {var} env variable")
- _path = Path(os.getenv(var, ""))
- _pExe = _path / "python.exe"
- if not _pExe.is_file():
- log.warning(f"Can't find python.exe from {_pExe}, using default python3")
- _pExe = Path(os.getenv("PYTHON3_32_PATH")) / "python.exe"
- else:
- _pExe = Path(os.getenv("PYTHON2_32_PATH")) / "python.exe"
- else:
- if python_ver.startswith("3"):
- var = f"PYTHON{python_ver}-64_PATH"
- log.info(f"Try to find python from {var} env variable")
- _path = Path(os.getenv(var, ""))
- _pExe = _path / "python.exe"
- if not _pExe.is_file():
- log.warning(f"Can't find python.exe from {_pExe}, using default python3")
- _pExe = Path(os.getenv("PYTHON3_PATH")) / "python.exe"
- env_python = f"{_env}\\Scripts\\python.exe"
- env_pip = f"{_env}\\Scripts\\pip.exe"
- else:
- _pExe = f"python{python_ver}"
- try:
- run_instruction([_pExe, "--version"], f"Failed to guess python version {_pExe}")
- except Exception as e:
- print(f"Exception {type(e).__name__}: {e}")
- _pExe = "python3"
- return (_pExe, _env, env_pip, env_python)
-
-
def run_instruction(instruction, error, initial_env=None):
if initial_env is None:
initial_env = os.environ
exit(result)
-def get_ci_qtpaths_path(ci_install_dir, ci_host_os):
- qtpaths_path = f"--qtpaths={ci_install_dir}"
- if ci_host_os == "MacOS":
- return f"{qtpaths_path}/bin/qtpaths"
- elif ci_host_os == "Windows":
- return f"{qtpaths_path}\\bin\\qtpaths.exe"
- else:
- return f"{qtpaths_path}/bin/qtpaths"
-
-
-def get_ci_qmake_path(ci_install_dir, ci_host_os):
- qmake_path = f"--qmake={ci_install_dir}"
- if ci_host_os == "MacOS":
- return f"{qmake_path}/bin/qmake"
- elif ci_host_os == "Windows":
- return f"{qmake_path}\\bin\\qmake.exe"
- else:
- return f"{qmake_path}/bin/qmake"
-
-
def parse_cmake_conf_assignments_by_key(source_dir):
"""
Parses a .cmake.conf file that contains set(foo "bar") assignments
copydir(qt_meta_types_dir, destination_qt_dir / rel_meta_data_dir,
_filter=["*.json"],
recursive=False, _vars=_vars, force_copy_symlinks=True)
+
+
+def in_coin():
+ return os.environ.get('COIN_UNIQUE_JOB_ID', None) is not None
+
+
+def parse_modules(modules: str) -> str:
+ module_sub_set = ""
+ for m in modules.split(','):
+ if m.startswith('Qt'):
+ m = m[2:]
+ if module_sub_set:
+ module_sub_set += ';'
+ module_sub_set += m
+ return module_sub_set
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import json
import sys
from dataclasses import Field, dataclass, field
-from typing import Dict, List
_pyside_package_path = None
_module_json_file_path = None
+_multimedia_libs = ["avcodec", "avformat", "avutil", "swresample", "swscale"]
def set_pyside_package_path(p):
name: str
ext: str = ""
# Libraries not related to Qt modules
- lib: List[str] = field(default_factory=list)
+ lib: list[str] = field(default_factory=list)
# Libraries related to Qt modules
- qtlib: List[str] = field(default_factory=list)
+ qtlib: list[str] = field(default_factory=list)
# Files from the Qt/qml directory
- qml: List[str] = field(default_factory=list)
- pyi: List[str] = field(default_factory=list)
- translations: List[str] = field(default_factory=list)
- typesystems: List[str] = field(default_factory=list)
- include: List[str] = field(default_factory=list)
- glue: List[str] = field(default_factory=list)
- metatypes: List[str] = field(default_factory=list)
- plugins: List[str] = field(default_factory=list)
+ qml: list[str] = field(default_factory=list)
+ pyi: list[str] = field(default_factory=list)
+ translations: list[str] = field(default_factory=list)
+ typesystems: list[str] = field(default_factory=list)
+ include: list[str] = field(default_factory=list)
+ glue: list[str] = field(default_factory=list)
+ doc_glue: list[str] = field(default_factory=list)
+ metatypes: list[str] = field(default_factory=list)
+ plugins: list[str] = field(default_factory=list)
# For special cases when a file/directory doesn't fall into
# the previous categories.
- extra_dirs: List[str] = field(default_factory=list)
- extra_files: List[str] = field(default_factory=list)
+ extra_dirs: list[str] = field(default_factory=list)
+ extra_files: list[str] = field(default_factory=list)
# Once the object is created, this method will be executed
# and automatically will initialize some of the files that are
self.typesystems.append(f"typesystem_{_lo}.xml")
self.include.append(f"Qt{self.name}/*.h")
self.glue.append(f"qt{_lo}.cpp")
+ self.doc_glue.append(f"qt{_lo}.rst")
if not len(self.metatypes):
self.metatypes.append(f"qt6{_lo}_relwithdebinfo_metatypes.json")
self.typesystems = [f"typesystems/{i}" for i in self.typesystems]
self.include = [f"include/{i}" for i in self.include]
self.glue = [f"glue/{i}" for i in self.glue]
+ self.doc_glue = [f"doc/{i}" for i in self.doc_glue]
def macos_pyside_wrappers_lib(self, s):
if s.startswith("Qt"):
return f"{s}.*{self.ext}*"
@classmethod
- def get_fields(cls) -> Dict[str, Field]:
+ def get_fields(cls) -> dict[str, Field]:
return cls.__dataclass_fields__
@staticmethod
# for each module that will be included in the wheel.
# PySide wheel
-def wheel_files_pyside_essentials() -> List[ModuleData]:
+def wheel_files_pyside_essentials() -> list[ModuleData]:
files = [
module_QtCore(),
module_QtGui(),
# PySide Addons wheel
-def wheel_files_pyside_addons() -> List[ModuleData]:
+def wheel_files_pyside_addons() -> list[ModuleData]:
files = [
module_Qt3DAnimation(),
module_Qt3DCore(),
module_QtCharts(),
module_QtDataVisualization(),
module_QtGraphs(),
+ module_QtGraphsWidgets(),
module_QtMultimedia(),
module_QtMultimediaWidgets(),
module_QtNetworkAuth(),
module_QtHttpServer(),
module_QtLocation(),
module_QtAsyncio(),
+ module_QtWebView(),
+ # This is not an actual module, but it's required in order
+ # to add the 'Quick' components of the WebView.
+ module_QtWebViewQuick(),
]
return files
"libQt6QmlNetwork",
"libQt6QmlWorkerScript",
"libQt6QmlXmlListModel",
- "libQt6QmlCompiler"
+ "libQt6QmlCompiler",
+ "libQt6QmlMeta",
+ "libQt6LabsPlatform",
]
_include = [
"qt6qmlmodels_relwithdebinfo_metatypes.json",
"qt6qmlworkerscript_relwithdebinfo_metatypes.json",
"qt6qmlxmllistmodel_relwithdebinfo_metatypes.json",
+ "qt6qmlmeta_relwithdebinfo_metatypes.json",
+ "qt6labsplatform_relwithdebinfo_metatypes.json",
]
_qml = [
"qt6quicktest_relwithdebinfo_metatypes.json",
"qt6quicktestutilsprivate_relwithdebinfo_metatypes.json",
"qt6quicktimeline_relwithdebinfo_metatypes.json",
+ "qt6quickvectorimage_relwithdebinfo_metatypes.json",
+ "qt6quickvectorimagegeneratorprivate_relwithdebinfo_metatypes.json",
]
_qtlib = [
"libQt6QuickEffects",
"libQt6QuickTest",
"libQt6QuickTimeline",
"libQt6QuickTimelineBlendTrees",
+ "libQt6QuickVectorImage",
+ "libQt6QuickVectorImageGenerator"
]
- # Adding GraphicalEffects files
- data.qml.append("Qt5Compat/GraphicalEffects")
-
data.qtlib.extend(_qtlib)
data.metatypes.extend(_metatypes)
json_data = get_module_json_data("Quick")
data.plugins = get_module_plugins(json_data)
+ data.extra_files.append("svgtoqml*")
return data
data.qtlib.append("libQt6QuickControls2MaterialStyleImpl")
data.qtlib.append("libQt6QuickControls2Universal")
data.qtlib.append("libQt6QuickControls2UniversalStyleImpl")
+ # FluentWinUI3 Style is available for all platforms, even if it
+ # was originally intended for Windows.
+ data.qtlib.append("libQt6QuickControls2FluentWinUI3StyleImpl")
if sys.platform == "win32":
data.qtlib.append("libQt6QuickControls2WindowsStyleImpl")
elif sys.platform == "darwin":
"libQt63DQuickInput",
"libQt63DQuickRender",
"libQt63DQuickScene2D",
+ "libQt6Quick3DXr",
]
_metatypes = [
"qt6quick3druntimerender_relwithdebinfo_metatypes.json",
"qt6quick3dutils_relwithdebinfo_metatypes.json",
"qt6shadertools_relwithdebinfo_metatypes.json",
+ "qt6quick3dxr_relwithdebinfo_metatypes.json"
]
json_data = get_module_json_data("Quick3DAssetImport")
data.extra_files.append("qsb*")
data.extra_files.append("balsam*")
+ # Adding GraphicalEffects files
+ data.qml.append("Qt5Compat/GraphicalEffects")
+
return data
return data
+def module_QtGraphsWidgets() -> ModuleData:
+ data = ModuleData("GraphsWidgets")
+
+ return data
+
+
def module_QtMultimedia() -> ModuleData:
data = ModuleData("Multimedia")
data.qtlib.append("libQt6MultimediaQuick")
data.translations.append("qtmultimedia_*")
data.plugins = get_module_plugins(json_data)
- if sys.platform == "win32":
- data.extra_files.extend(["avcodec-60.dll", "avformat-60.dll", "avutil-58.dll",
- "swresample-4.dll", "swscale-7.dll"])
+ linux_multimedia_libs = [f"Qt/lib/lib{lib}*.so*" for lib in _multimedia_libs]
+ linux_multimedia_libs.append("Qt/lib/libQt6FFmpegStub*.so*")
+ platform_files = {
+ "win32": [f"{lib}-*.dll" for lib in _multimedia_libs],
+ "darwin": [f"Qt/lib/lib{lib}.*.dylib" for lib in _multimedia_libs],
+ "linux": linux_multimedia_libs}
+ extra_files = platform_files.get(sys.platform, [])
+ data.extra_files.extend(extra_files)
return data
def module_QtExampleIcons() -> ModuleData:
data = ModuleData("ExampleIcons")
return data
+
+
+def module_QtWebView() -> ModuleData:
+ data = ModuleData("WebView")
+ json_data = get_module_json_data("WebView")
+ data.plugins = get_module_plugins(json_data)
+ return data
+
+
+def module_QtWebViewQuick() -> ModuleData:
+ data = ModuleData("WebViewQuick")
+ return data
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import platform
def finalize_options(self):
CommandMixin.mixin_finalize_options(self)
- if sys.platform == 'darwin':
+ if sys.platform == 'darwin' and not self.is_cross_compile:
# Override the platform name to contain the correct
# minimum deployment target.
# This is used in the final wheel name.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import time
from pathlib import Path
product_dependency:
../../qt/qt5:
- ref: "3f005f1e2e88485dbf541200ba3fafcad6ea84ad"
+ ref: "480041bb0bfd400f29dd49facfaa924dacac5c03"
dependency_source: supermodule
dependencies: [
"../../qt/qt3d",
"../../qt/qtwayland",
"../../qt/qtwebchannel",
"../../qt/qtwebengine",
- "../../qt/qtwebsockets"
+ "../../qt/qtwebsockets",
+ "../../qt/qtwebview",
]
--- /dev/null
+#!/bin/bash
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+# Download the file
+wget -q https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_18.1.7-based-linux-Debian-11.6-gcc10.2-arm64.7z
+if [ $? -ne 0 ]; then
+ echo "Error: Failed to download libclang archive" >&2
+ exit 1
+fi
+
+# Unzip the contents to /home/qt
+7z x libclang-release_18.1.7-based-linux-Debian-11.6-gcc10.2-arm64.7z -o/home/qt
+if [ $? -ne 0 ]; then
+ echo "Error: Failed to extract libclang archive" >&2
+ exit 1
+fi
+
+# Remove the 7z file after extraction
+rm libclang-release_18.1.7-based-linux-Debian-11.6-gcc10.2-arm64.7z
+if [ $? -ne 0 ]; then
+ echo "Error: Failed to remove libclang archive" >&2
+ exit 1
+fi
variableValue: "/Users/qt/.local/bin/:"
enable_if:
condition: property
- property: host.osVersion
- equals_value: MacOS_11_00
+ property: host.os
+ equals_value: MacOS
- type: PrependToEnvironmentVariable
variableName: PATH
variableValue: "/Users/qt/work/install/bin:"
condition: property
property: target.compiler
equals_value: ICC_18
- - type: EnvironmentVariable
- variableName: LLVM_INSTALL_DIR
- variableValue: "{{.Env.LLVM_DYNAMIC_LIBS_100}}"
- disable_if:
- condition: and
- conditions:
- - condition: property
- property: target.arch
- equals_value: X86_64-ARM64 # When target arch is universal binary, we can use the default libclang
- - condition: property
- property: host.os
- equals_value: MacOS
- type: ExecuteCommand
- command: "sudo apt-get install python3-pip libclang-11-dev clang -y"
+ command: "sudo apt-get install python3-pip -y"
maxTimeInSeconds: 14400
maxTimeBetweenOutput: 1200
enable_if:
userMessageOnFailure: >
Failed to install dependencies
- type: ExecuteCommand
- command: "python3 -m pip install -U setuptools==69.1.1"
+ command: "chmod +x coin/fetch_libclang_arm64.sh"
maxTimeInSeconds: 14400
maxTimeBetweenOutput: 1200
enable_if:
property: host.arch
equals_value: AARCH64
userMessageOnFailure: >
- Failed to install setuptools
+ Failed to make coin/fetch_libclang_arm64.sh executable
+ - type: ExecuteCommand
+ command: "coin/fetch_libclang_arm64.sh"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 1200
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: host.os
+ equals_value: Linux
+ - condition: property
+ property: host.arch
+ equals_value: AARCH64
+ userMessageOnFailure: >
+ Failed to download libclang from Qt servers
- type: EnvironmentVariable
variableName: LLVM_INSTALL_DIR
- variableValue: "/usr/lib/llvm-11/lib"
+ variableValue: "/home/qt/libclang"
enable_if:
condition: and
conditions:
enable_if:
condition: property
property: host.osVersion
- in_values: [RHEL_8_6, RHEL_8_8]
+ in_values: [RHEL_8_6, RHEL_8_8, RHEL_8_10]
- type: EnvironmentVariable
variableName: interpreter
variableValue: "python3"
enable_if: # Enable for others than rhl 8.*
condition: property
- property: target.osVersion
- not_in_values: [RHEL_8_6, RHEL_8_8]
+ property: host.osVersion
+ not_in_values: [RHEL_8_6, RHEL_8_8, RHEL_8_10]
+ # ToDo: can be removed after 3.11 is available on qt5#3.8 on macOS
+ # start of ToDo
+ - type: ExecuteCommand
+ command: "tools/install-p311.sh"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 1200
+ ignoreExitCode: true
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: host.os
+ equals_value: MacOS
+ - condition: property
+ property: target.os
+ equals_value: Android
+ userMessageOnFailure: >
+ Failed to install python 3.11
+ - type: PrependToEnvironmentVariable
+ variableName: PATH
+ variableValue: "/Users/qt/python311/bin:"
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: host.os
+ equals_value: MacOS
+ - condition: property
+ property: target.os
+ equals_value: Android
+ # end of ToDo
+ - type: ExecuteCommand
+ command: "virtualenv -p {{.Env.interpreter}} {{.AgentWorkingDir}}/env"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 1200
+ enable_if:
+ condition: or
+ conditions:
+ - condition: property
+ property: host.os
+ equals_value: Linux
+ - condition: property
+ property: host.os
+ equals_value: MacOS
+ userMessageOnFailure: >
+ Failed to create env
+ - type: EnvironmentVariable
+ variableName: ENV_PYTHON
+ variableValue: "{{.AgentWorkingDir}}/env/bin/python"
+ - type: AppendToEnvironmentVariable
+ variableName: PATH
+ variableValue: ":{{.AgentWorkingDir}}/env/bin/"
+ enable_if: # Enable for others than rhl 8.*
+ condition: property
+ property: target.os
+ equals_value: Android
+
+
--- /dev/null
+type: Group
+instructions:
+ - type: ExecuteCommand
+ command: "{{.Env.interpreter}} -m pip install --user -r requirements.txt"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 1200
+ enable_if:
+ condition: or
+ conditions:
+ - condition: property
+ property: host.os
+ equals_value: Linux
+ - condition: property
+ property: host.os
+ equals_value: MacOS
+ userMessageOnFailure: >
+ Failed to install deps
+ - type: ExecuteCommand
+ command: "{{.Env.interpreter}} -m pip install --user -r tools/cross_compile_android/requirements.txt"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 1200
+ ignoreExitCode: true
+ enable_if:
+ condition: or
+ conditions:
+ - condition: property
+ property: host.os
+ equals_value: Linux
+ - condition: property
+ property: host.os
+ equals_value: MacOS
+ userMessageOnFailure: >
+ Failed to install deps
+ - type: ExecuteCommand
+ command: "{{.Env.interpreter}} tools/cross_compile_android/main.py --qt-install-path /Users/qt/work/install --auto-accept-license --skip-update --verbose --ndk-path {{.Env.ANDROID_NDK_ROOT_DEFAULT}} --sdk-path {{.Env.ANDROID_SDK_ROOT}} --plat-name aarch64 --coin"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 1200
+ ignoreExitCode: true
+ enable_if:
+ condition: property
+ property: host.os
+ equals_value: MacOS
+ userMessageOnFailure: >
+ Failed to execute build instructions on macOS
+ - type: ExecuteCommand
+ command: "{{.Env.interpreter}} tools/cross_compile_android/main.py --qt-install-path /home/qt/work/install --auto-accept-license --skip-update --verbose --ndk-path {{.Env.ANDROID_NDK_ROOT_DEFAULT}} --sdk-path {{.Env.ANDROID_SDK_ROOT}} --plat-name x86_64 --coin"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 1200
+ ignoreExitCode: true
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: host.os
+ equals_value: Linux
+ - condition: property
+ property: target.arch
+ equals_value: X86_64
+ userMessageOnFailure: >
+ Failed to execute build instructions on Linux
+ # ToDo: Do we need x86 build?
+ # - type: ExecuteCommand
+ # command: "{{.Env.interpreter}} tools/cross_compile_android/main.py --qt-install-path /home/qt/work/install --auto-accept-license --skip-update --verbose --ndk-path {{.Env.ANDROID_NDK_ROOT_DEFAULT}} --sdk-path {{.Env.ANDROID_SDK_ROOT}} --plat-name x86 --clean-cache all --coin"
+ # maxTimeInSeconds: 14400
+ # maxTimeBetweenOutput: 1200
+ # enable_if:
+ # condition: and
+ # conditions:
+ # - condition: property
+ # property: host.os
+ # equals_value: Linux
+ # - condition: property
+ # property: target.arch
+ # equals_value: X86
+ # userMessageOnFailure: >
+ # Failed to execute build instructions on Linux
type: Group
instructions:
- - type: ExecuteCommand
- command: "python3 -m pip install -U setuptools==69.1.1"
- maxTimeInSeconds: 14400
- maxTimeBetweenOutput: 1200
+ - type: Group
+ instructions:
+ - !include "{{pyside/pyside-setup}}/execute_android_instructions.yaml"
enable_if:
- condition: property
- property: host.os
- equals_value: MacOS
- userMessageOnFailure: >
- Failed to install setuptools on macOS
- - type: ExecuteCommand
- command: "python3 -u coin_build_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} {{.Env.CI_USE_SCCACHE}} --instdir=/Users/qt/work/install --targetOs={{.Env.CI_OS}} --hostArch=X86_64 --targetArch={{.Env.CI_TARGET_ARCHITECTURE}} --phase=ALL"
- maxTimeInSeconds: 14400
- maxTimeBetweenOutput: 1200
- enable_if:
- condition: and
- conditions:
- - condition: property
- property: target.arch
- equals_value: X86_64
- - condition: property
- property: host.os
- equals_value: MacOS
- userMessageOnFailure: >
- Failed to execute build instructions on macOS
- - type: ExecuteCommand
- command: "python3 -u coin_build_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} {{.Env.CI_USE_SCCACHE}} --instdir=/Users/qt/work/install --targetOs={{.Env.CI_OS}} --hostArch=X86_64 --targetArch=X86_64-ARM64 --phase=ALL"
- maxTimeInSeconds: 14400
- maxTimeBetweenOutput: 1200
+ condition: property
+ property: target.os
+ equals_value: Android
+ - type: Group
+ instructions:
+ - !include "{{pyside/pyside-setup}}/execute_desktop_instructions.yaml"
enable_if:
condition: and
conditions:
- condition: property
- property: target.arch
- equals_value: X86_64-ARM64
- - condition: property
- property: host.os
- equals_value: MacOS
- userMessageOnFailure: >
- Failed to execute build instructions on macOS
- - type: EnvironmentVariable
- variableName: PYSIDE_SIGNING_DIR
- variableValue: "{{.AgentWorkingDir}}\\pyside\\{{.Env.TESTED_MODULE_COIN}}\\build\\qfpa-p3.10\\package_for_wheels"
- enable_if:
- condition: property
- property: host.os
- equals_value: Windows
- - type: ExecuteCommand
- command: "{{.Env.interpreter}} -m pip install -U pip setuptools==69.1.1 --user"
- maxTimeInSeconds: 14400
- maxTimeBetweenOutput: 1200
- enable_if:
- condition: property
- property: host.os
- equals_value: Linux
- userMessageOnFailure: >
- Failed to install setuptools on Linux
- - type: ExecuteCommand
- command: "{{.Env.interpreter}} -u coin_build_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} {{.Env.CI_USE_SCCACHE}} --instdir=/home/qt/work/install --targetOs={{.Env.CI_OS}} --hostArch={{.Env.HOST_ARCH_COIN}} --targetArch={{.Env.TARGET_ARCH_COIN}}--phase=ALL"
- maxTimeInSeconds: 14400
- maxTimeBetweenOutput: 600
- enable_if:
- condition: property
- property: host.os
- equals_value: Linux
- userMessageOnFailure: >
- Failed to execute build instructions on Linux
- - type: ExecuteCommand
- command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -m pip install -U setuptools==69.1.1"
- maxTimeInSeconds: 14400
- maxTimeBetweenOutput: 1200
- enable_if:
- condition: property
- property: host.os
- equals_value: Windows
- userMessageOnFailure: >
- Failed to install setuptools on Windows
- - type: ExecuteCommand
- command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -u coin_build_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} {{.Env.CI_USE_SCCACHE}} --instdir=\\Users\\qt\\work\\install --targetOs={{.Env.CI_OS}} --hostArch=X86_64 --targetArch={{.Env.CI_TARGET_ARCHITECTURE}} --phase=BUILD"
- maxTimeInSeconds: 14400
- maxTimeBetweenOutput: 600
- enable_if:
- condition: property
- property: host.os
- equals_value: Windows
- userMessageOnFailure: >
- Failed to execute build instructions on Windows
- - type: SignPackage
- directory: "{{.Env.PYSIDE_SIGNING_DIR}}"
- maxTimeInSeconds: 1200
- maxTimeBetweenOutput: 1200
- enable_if:
- condition: property
- property: host.os
- equals_value: Windows
- - type: ChangeDirectory
- directory: "{{.AgentWorkingDir}}\\pyside\\pyside-setup"
- enable_if:
- condition: property
- property: host.os
- equals_value: Windows
- - type: ExecuteCommand
- command: "c:\\users\\qt\\MSVC.bat {{.Env.PYSIDE_VIRTUALENV}}\\Scripts\\python.exe create_wheels.py"
- maxTimeInSeconds: 14400
- maxTimeBetweenOutput: 300
- enable_if:
- condition: property
- property: host.os
- equals_value: Windows
- userMessageOnFailure: >
- Failed to create wheels for Windows
- - type: ExecuteCommand
- command: "{{.Env.PYSIDE_VIRTUALENV}}/bin/python create_wheels.py"
- maxTimeInSeconds: 14400
- maxTimeBetweenOutput: 300
- enable_if:
- condition: or
- conditions:
- - condition: property
- property: host.os
- equals_value: MacOS
+ property: host.compiler
+ not_equals_value: Mingw
- condition: property
- property: host.os
- equals_value: Linux
- userMessageOnFailure: >
- Failed to create wheels
+ property: target.os
+ not_equals_value: Android
--- /dev/null
+type: Group
+instructions:
+ - type: ExecuteCommand
+ command: "python3 -m pip install -r requirements-coin.txt"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 1200
+ enable_if:
+ condition: property
+ property: host.os
+ equals_value: MacOS
+ userMessageOnFailure: >
+ Failed to install requirements-coin.txt dependencies on macOS
+ - type: ExecuteCommand
+ command: "python3 -u coin_build_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} {{.Env.CI_USE_SCCACHE}} --instdir=/Users/qt/work/install --targetOs={{.Env.CI_OS}} --hostArch=X86_64 --targetArch={{.Env.CI_TARGET_ARCHITECTURE}} --phase=ALL"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 1200
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: target.arch
+ equals_value: X86_64
+ - condition: property
+ property: host.os
+ equals_value: MacOS
+ userMessageOnFailure: >
+ Failed to execute build instructions on macOS
+ - type: ExecuteCommand
+ command: "python3 -u coin_build_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} {{.Env.CI_USE_SCCACHE}} --instdir=/Users/qt/work/install --targetOs={{.Env.CI_OS}} --hostArch=X86_64 --targetArch=X86_64-ARM64 --phase=ALL"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 1200
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: target.arch
+ equals_value: X86_64-ARM64
+ - condition: property
+ property: host.os
+ equals_value: MacOS
+ userMessageOnFailure: >
+ Failed to execute build instructions on macOS
+ - type: EnvironmentVariable
+ variableName: PYSIDE_SIGNING_DIR
+ variableValue: "{{.AgentWorkingDir}}\\pyside\\{{.Env.TESTED_MODULE_COIN}}\\build\\qfpa-p3.10\\package_for_wheels"
+ enable_if:
+ condition: property
+ property: host.os
+ equals_value: Windows
+ - type: ExecuteCommand
+ command: "{{.Env.interpreter}} -m pip install -r requirements-coin.txt --user"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 1200
+ enable_if:
+ condition: and
+ conditions:
+ - condition: property
+ property: host.os
+ equals_value: Linux
+ - condition: property
+ property: host.osVersion
+ not_equals_value: Ubuntu_24_04
+ userMessageOnFailure: >
+ Failed to install requirements-coin.txt dependencies on Linux
+ - type: ExecuteCommand
+ command: "{{.Env.interpreter}} -u coin_build_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} {{.Env.CI_USE_SCCACHE}} --instdir=/home/qt/work/install --targetOs={{.Env.CI_OS}} --hostArch={{.Env.HOST_ARCH_COIN}} --targetArch={{.Env.TARGET_ARCH_COIN}}--phase=ALL"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 600
+ enable_if:
+ condition: property
+ property: host.os
+ equals_value: Linux
+ userMessageOnFailure: >
+ Failed to execute build instructions on Linux
+ - type: ExecuteCommand
+ command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -m pip install -r requirements-coin.txt"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 1200
+ enable_if:
+ condition: property
+ property: host.os
+ equals_value: Windows
+ userMessageOnFailure: >
+ Failed to install requirements-coin.txt dependencies on Windows
+ - type: ExecuteCommand
+ command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -u coin_build_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} {{.Env.CI_USE_SCCACHE}} --instdir=\\Users\\qt\\work\\install --targetOs={{.Env.CI_OS}} --hostArch=X86_64 --targetArch={{.Env.CI_TARGET_ARCHITECTURE}} --phase=BUILD"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 600
+ enable_if:
+ condition: property
+ property: host.os
+ equals_value: Windows
+ userMessageOnFailure: >
+ Failed to execute build instructions on Windows
+ - type: SignPackage
+ directory: "{{.Env.PYSIDE_SIGNING_DIR}}"
+ maxTimeInSeconds: 1200
+ maxTimeBetweenOutput: 1200
+ enable_if:
+ condition: property
+ property: host.os
+ equals_value: Windows
+ - type: ChangeDirectory
+ directory: "{{.AgentWorkingDir}}\\pyside\\pyside-setup"
+ enable_if:
+ condition: property
+ property: host.os
+ equals_value: Windows
+ - type: ExecuteCommand
+ command: "c:\\users\\qt\\MSVC.bat {{.Env.PYSIDE_VIRTUALENV}}\\Scripts\\python.exe create_wheels.py"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 300
+ enable_if:
+ condition: property
+ property: host.os
+ equals_value: Windows
+ userMessageOnFailure: >
+ Failed to create wheels for Windows
+ - type: ExecuteCommand
+ command: "{{.Env.PYSIDE_VIRTUALENV}}/bin/python create_wheels.py"
+ maxTimeInSeconds: 14400
+ maxTimeBetweenOutput: 300
+ enable_if:
+ condition: or
+ conditions:
+ - condition: property
+ property: host.os
+ equals_value: MacOS
+ - condition: property
+ property: host.os
+ equals_value: Linux
+ userMessageOnFailure: >
+ Failed to create wheels
+++ /dev/null
-type: Group
-instructions:
- - type: ChangeDirectory
- directory: "{{.AgentWorkingDir}}"
- - type: InstallSourceArchive
- maxTimeInSeconds: 600
- maxTimeBetweenOutput: 600
- project: qt/qtbase
- ref: "CURRENT_BRANCH"
- directory: qt/qtbase
- userMessageOnFailure: >
- Could not install source archive. Please investigate why.
- - type: InstallSourceArchive
- maxTimeInSeconds: 600
- maxTimeBetweenOutput: 600
- project: qt/qtqa
- ref: master
- directory: qt/qtqa-latest
- userMessageOnFailure: >
- Could not install source archive. Please investigate why.
- - type: EnvironmentVariable
- variableName: QT_MODULE_TO_TEST
- variableValue: pyside/pyside-setup
- userMessageOnFailure: >
- Failed to set environment variable. This should not happen.
- - type: ExecuteCommand
- command: perl qt/qtqa-latest/tests/prebuild/license/tst_licenses.pl
- maxTimeInSeconds: 7200
- maxTimeBetweenOutput: 120
- userMessageOnFailure: >
- Failed to run license check script.
not_contains_value: LicenseCheck
instructions:
- type: ExecuteCommand
- command: "python3 -m pip install -U setuptools==69.1.1"
+ command: "python3 -m pip install -r requirements-coin.txt"
maxTimeInSeconds: 14400
maxTimeBetweenOutput: 1200
enable_if:
property: host.os
equals_value: MacOS
userMessageOnFailure: >
- Failed to install setuptools on macOS
+ Failed to install requirements-coin.txt on macOS
- type: ExecuteCommand
command: "python3 -u coin_test_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} --instdir=/Users/qt/work/install --targetOs={{.Env.CI_OS}} --hostArch=ARM64 --targetArch={{.Env.CI_TARGET_ARCHITECTURE}}"
maxTimeInSeconds: 14400
condition: and
conditions:
- condition: property
- property: host.osVersion
- equals_value: MacOS_11_00
+ property: host.os
+ equals_value: MacOS
- condition: property
property: host.arch
equals_value: ARM64
+ - condition: property
+ property: target.os
+ not_equals_value: Android
userMessageOnFailure: >
Failed to execute test instructions on arm mac
- type: ExecuteCommand
- condition: property
property: host.arch
equals_value: X86_64
+ - condition: property
+ property: target.os
+ not_equals_value: Android
userMessageOnFailure: >
Failed to execute test instructions on macOS
- type: ExecuteCommand
- command: "{{.Env.interpreter}} -m pip install -U pip setuptools==69.1.1 --user"
+ command: "{{.Env.interpreter}} -m pip install -r requirements-coin.txt --user"
maxTimeInSeconds: 14400
maxTimeBetweenOutput: 1200
enable_if:
property: host.os
equals_value: Linux
userMessageOnFailure: >
- Failed to install setuptools on Linux
+ Failed to install requirements-coin.txt on Linux
- type: ExecuteCommand
command: "{{.Env.interpreter}} -u coin_test_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} --instdir=/home/qt/work/install --targetOs={{.Env.CI_OS}} --hostArch=X86_64 --targetArch={{.Env.CI_TARGET_ARCHITECTURE}}"
maxTimeInSeconds: 14400
maxTimeBetweenOutput: 600
enable_if:
- condition: property
- property: host.os
- equals_value: Linux
+ condition: and
+ conditions:
+ - condition: property
+ property: host.os
+ equals_value: Linux
+ - condition: property
+ property: target.os
+ not_equals_value: Android
userMessageOnFailure: >
Failed to execute test instructions on Linux
- type: ExecuteCommand
- command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -m pip install -U pip setuptools==69.1.1 --user"
+ command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -m pip install -r requirements-coin.txt --user"
maxTimeInSeconds: 14400
maxTimeBetweenOutput: 1200
enable_if:
property: host.os
equals_value: Windows
userMessageOnFailure: >
- Failed to install setuptools on Windows
+ Failed to install requirements-coin.txt on Windows
- type: ExecuteCommand
command: "c:\\users\\qt\\MSVC.bat {{.Env.PYTHON3_PATH}}\\python.exe -u coin_test_instructions.py --os={{.Env.CI_OS}} {{.Env.CI_PACKAGING_FEATURE}} --instdir=c:\\Users\\qt\\work\\install --targetOs={{.Env.CI_OS}} --hostArch=X86_64 --targetArch={{.Env.CI_TARGET_ARCHITECTURE}}"
maxTimeInSeconds: 14400
maxTimeBetweenOutput: 600
enable_if:
- condition: property
- property: host.os
- equals_value: Windows
+ condition: and
+ conditions:
+ - condition: property
+ property: host.os
+ equals_value: Windows
+ - condition: property
+ property: target.os
+ not_equals_value: Android
+ - condition: property
+ property: host.compiler
+ not_equals_value: Mingw
userMessageOnFailure: >
Failed to execute test instructions on 64 bit Windows
--- /dev/null
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+import calendar
+import datetime
+import os
+import site
+import sys
+from pathlib import Path
+
+from build_scripts.options import Options
+from build_scripts.utils import (parse_cmake_conf_assignments_by_key,
+ remove_tree, run_instruction)
+
+options = Options()
+
+
+class CI:
+ def __init__(self):
+ # Values must match COIN thrift
+ self.HOST_OS = options.option_value("os")
+ self.TARGET_OS = options.option_value("targetOs")
+ self.HOST_ARCH = options.option_value("hostArch")
+ self.TARGET_ARCH = options.option_value("targetArch")
+ self.HOST_OS_VER = options.option_value("osVer")
+ self.ENV_INSTALL_DIR = options.option_value("instdir")
+ self.ENV_AGENT_DIR = options.option_value("agentdir") or "."
+ self.COMPILER = options.option_value("compiler")
+ self.USE_SCCACHE = options.option_value("compiler-launcher")
+ self.INTEGRATION_ID = options.option_value("coinIntegrationId") or str(
+ calendar.timegm(datetime.datetime.now().timetuple())
+ )
+ self.FEATURES = []
+ _ci_features = options.option_value("features")
+ if _ci_features is not None:
+ for f in _ci_features.split(", "):
+ self.FEATURES.append(f)
+ self.RELEASE_CONF = options.has_option("packaging")
+ self.TEST_PHASE = options.option_value("phase")
+ if self.TEST_PHASE not in ["ALL", "BUILD"]:
+ self.TEST_PHASE = "ALL"
+
+
+def get_ci_exe_path(ci_install_dir, ci_host_os, qtexe):
+ """
+ qtexe can only be 'qmake' or 'qtpaths'
+ """
+ ext = ""
+ if ci_host_os == "Windows":
+ ext = ".exe"
+
+ _path = Path(ci_install_dir) / "bin" / f"{qtexe}{ext}"
+
+ return f"--{qtexe}={_path}"
+
+
+def get_env_or_raise(name: str) -> str:
+ o = os.getenv(name)
+ if o is None:
+ raise Exception(f"Variable not defined: {name}")
+ return o
+
+
+def get_qtci_virtualenv(python_ver, log, host, host_arch, target_arch):
+ _exe = "python"
+ _env = os.environ.get("PYSIDE_VIRTUALENV") or f"env{python_ver}"
+ env_python = f"{_env}/bin/python"
+ env_pip = f"{_env}/bin/pip"
+
+ if host == "Windows":
+ log.info("New virtualenv to build {target_arch} in {host_arch} host")
+ _exe = "python.exe"
+ if python_ver.startswith("3"):
+ var = f"PYTHON{python_ver}-64_PATH"
+ log.info(f"Try to find python from {var} env variable")
+ _path = Path(os.getenv(var, ""))
+ _exe = _path / "python.exe"
+ if not _exe.is_file():
+ log.warning(f"Can't find python.exe from {_exe}, using default python3")
+ _exe = Path(get_env_or_raise("PYTHON3_PATH")) / "python.exe"
+ env_python = rf"{_env}\Scripts\python.exe"
+ env_pip = rf"{_env}\Scripts\pip.exe"
+ else:
+ _exe = f"python{python_ver}"
+ try:
+ run_instruction([_exe, "--version"], f"Failed to guess python version {_exe}")
+ except Exception as e:
+ print(f"Exception {type(e).__name__}: {e}")
+ _exe = "python3"
+ return (_exe, _env, env_pip, env_python)
+
+
+def get_current_script_path():
+ """Returns the absolute path containing this script."""
+ try:
+ this_file = __file__
+ except NameError:
+ this_file = sys.argv[0]
+ this_file = Path(this_file).resolve()
+ return this_file.parents[0]
+
+
+def is_snapshot_build():
+ """
+ Returns True if project needs to be built with --snapshot-build
+
+ This is true if the version found in .cmake.conf is not a
+ pre-release version (no alphas, betas).
+
+ This eliminates the need to remove the --snapshot-build option
+ on a per-release branch basis (less things to remember to do
+ for a release).
+ """
+ # This returns pyside-setup/coin/ so we go one level down
+ # to get the root of the repo
+ setup_script_dir = get_current_script_path()
+ pyside_project_dir = setup_script_dir / ".." / "sources" / "pyside6"
+
+ d = parse_cmake_conf_assignments_by_key(str(pyside_project_dir))
+ release_version_type = d.get("pyside_PRE_RELEASE_VERSION_TYPE")
+ pre_release_version = d.get("pyside_PRE_RELEASE_VERSION")
+ if pre_release_version and release_version_type:
+ return True
+ return False
+
+
+def get_architecture(ci):
+ return "32" if ci.TARGET_ARCH == "X86" else "64"
+
+
+def get_python_version(ci):
+ python_ver = "3"
+ if ci.TARGET_OS == "Linux" and ci.HOST_ARCH != "aarch64":
+ python_ver = "3.11"
+ elif ci.TARGET_OS == "Windows":
+ python_ver = "3.10.0"
+ return python_ver
+
+
+def remove_variables(vars):
+ for env_var in vars:
+ if os.environ.get(env_var):
+ del os.environ[env_var]
+
+
+def setup_virtualenv(python, exe, env, pip, log):
+ # Within Ubuntu 24.04 one can't install anything with pip to outside of
+ # virtual env. Trust that we already have proper virtualenv installed.
+ if os.environ.get("HOST_OSVERSION_COIN") != "ubuntu_24_04":
+ run_instruction(
+ [str(python), "-m", "pip", "install", "--user", "virtualenv==20.7.2"],
+ "Failed to pin virtualenv",
+ )
+ # installing to user base might not be in PATH by default.
+ env_path = Path(str(site.USER_BASE)) / "bin"
+ v_env = env_path / "virtualenv"
+ if sys.platform == "win32":
+ env_path = os.path.join(site.USER_BASE, "Scripts")
+ v_env = os.path.join(env_path, "virtualenv.exe")
+ try:
+ run_instruction([str(v_env), "--version"], "Using default virtualenv")
+ except Exception as e:
+ log.info("Failed to use the default virtualenv")
+ log.info(f"{type(e).__name__}: {e}")
+ v_env = "virtualenv"
+ run_instruction([str(v_env), "-p", str(exe), str(env)], "Failed to create virtualenv")
+ # Pip is always upgraded when CI template is provisioned,
+ # upgrading it in later phase may cause perm issue
+ run_instruction(
+ [str(pip), "install", "-r", "requirements.txt"], "Failed to install dependencies"
+ )
+
+
+def call_setup(python_ver, ci, phase, log, buildnro=0):
+ print("call_setup")
+ print("python_ver", python_ver)
+ print("phase", phase)
+ exe, env, pip, env_python = get_qtci_virtualenv(
+ python_ver, log, ci.HOST_OS, ci.HOST_ARCH, ci.TARGET_ARCH
+ )
+
+ if phase not in ["BUILD", "TEST"]:
+ sys.exit(1)
+
+ remove_tree(env, True)
+ # Pinning the virtualenv before creating one
+ # Use pip3 if possible while pip seems to install the virtualenv to wrong dir in some OS
+ python = "python3"
+ if sys.platform == "win32":
+ python = Path(get_env_or_raise("PYTHON3_PATH")) / "python.exe"
+
+ if phase == "BUILD":
+ setup_virtualenv(python, exe, env, pip, log)
+ elif phase == "TEST":
+
+ if ci.HOST_OS == "MacOS" and ci.HOST_ARCH == "ARM64":
+ v_env = "virtualenv"
+ run_instruction([str(v_env), "-p", str(exe), str(env)], "Failed to create virtualenv")
+ run_instruction(
+ [pip, "install", "-r", "requirements.txt"], "Failed to install dependencies"
+ )
+ else:
+ setup_virtualenv(python, exe, env, pip, log)
+ # Install distro to replace missing platform.linux_distribution() in python3.8
+ run_instruction([pip, "install", "distro"], "Failed to install distro")
+
+ if phase == "BUILD":
+ cmd = [
+ env_python,
+ "-u",
+ "setup.py",
+ "build",
+ "--standalone",
+ "--unity",
+ "--build-tests",
+ "--log-level=verbose",
+ "--limited-api=yes",
+ ]
+
+ if ci.TARGET_ARCH == "X86_64-ARM64":
+ cmd += ["--macos-arch='x86_64;arm64'"]
+
+ if ci.USE_SCCACHE:
+ cmd += [f"--compiler-launcher={ci.USE_SCCACHE}"]
+
+ if is_snapshot_build():
+ cmd += ["--snapshot-build"]
+
+ qtpaths_path = get_ci_exe_path(ci.ENV_INSTALL_DIR, ci.HOST_OS, "qtpaths")
+ cmd.append(qtpaths_path)
+
+ # Due to certain older CMake versions generating very long paths
+ # (at least with CMake 3.6.2) when using the export() function,
+ # pass the shorter paths option on Windows so we don't hit
+ # the path character length limit (260).
+ if ci.HOST_OS == "Windows":
+ cmd += ["--shorter-paths"]
+
+ cmd += ["--package-timestamp=" + ci.INTEGRATION_ID]
+
+ env = os.environ
+ run_instruction(cmd, "Failed to run setup.py for build", initial_env=env)
+ elif phase == "TEST":
+ cmd = [
+ env_python,
+ "testrunner.py",
+ "test",
+ "--blacklist",
+ "build_history/blacklist.txt",
+ f"--buildno={buildnro}",
+ ]
+ run_instruction(cmd, "Failed to run testrunner.py")
+
+ qmake_path = get_ci_exe_path(ci.ENV_INSTALL_DIR, ci.HOST_OS, "qmake")
+
+ # Try to install built wheels, and build some buildable examples.
+ if ci.RELEASE_CONF:
+ wheel_tester_path = os.path.join("testing", "wheel_tester.py")
+ # Run the test for the new set of wheels
+ cmd = [env_python, wheel_tester_path, qmake_path, "--wheels-dir=dist", "--new"]
+ run_instruction(cmd, "Error while running wheel_tester.py on new wheels")
- condition: property
property: target.os
not_contains_value: IOS
- - condition: and
- conditions:
- condition: property
property: host.osVersion
- equals_value: MacOS_11_00
- - condition: property
- property: host.arch
- equals_value: ARM64
- - condition: property
- property: features
- contains_value: TestOnly
- - condition: property
- property: features
- contains_value: Packaging
+ not_equals_value: MacOS_12
- condition: and # Restore LoA config
conditions:
- condition: property
- property: host.osVersion
- equals_value: Debian_11_6
+ property: host.os
+ equals_value: Linux
- condition: property
property: host.arch
equals_value: AARCH64
+ - condition: property
+ property: target.arch
+ equals_value: AARCH64
- condition: property
property: features
- not_contains_value: DebianPackaging
+ contains_value: Packaging
+ - condition: and
+ conditions:
+ - condition: property
+ property: target.os
+ equals_value: Android
- condition: property
property: features
contains_value: Packaging
-
machine_type:
Build:
cores: 8
instructions:
LicenseCheck:
- - !include "{{pyside/pyside-setup}}/execute_license_check.yaml"
+ - !include "{{qt/qtbase}}/run_license_check.yaml"
Build:
- !include "{{pyside/pyside-setup}}/common_environment.yaml"
- !include "{{pyside/pyside-setup}}/find_path_to_msvc_compiler.yaml"
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-import calendar
-import datetime
+from __future__ import annotations
+
import logging
import os
-import os.path
-import site
import sys
from build_scripts.log import log
-from build_scripts.options import has_option, option_value
-from build_scripts.utils import (expand_clang_variables, get_ci_qtpaths_path,
- get_qtci_virtualEnv,
- parse_cmake_conf_assignments_by_key,
- remove_tree, run_instruction)
-
-log.setLevel(logging.INFO)
-
-# Values must match COIN thrift
-CI_HOST_OS = option_value("os")
-CI_TARGET_OS = option_value("targetOs")
-CI_HOST_ARCH = option_value("hostArch")
-CI_TARGET_ARCH = option_value("targetArch")
-CI_HOST_OS_VER = option_value("osVer")
-CI_ENV_INSTALL_DIR = option_value("instdir")
-CI_ENV_AGENT_DIR = option_value("agentdir")
-CI_COMPILER = option_value("compiler")
-CI_USE_SCCACHE = option_value("compiler-launcher")
-CI_INTEGRATION_ID = option_value("coinIntegrationId") or str(
- calendar.timegm(datetime.datetime.now().timetuple())
-)
-CI_FEATURES = []
-_ci_features = option_value("features")
-if _ci_features is not None:
- for f in _ci_features.split(", "):
- CI_FEATURES.append(f)
-CI_RELEASE_CONF = has_option("packaging")
-CI_TEST_PHASE = option_value("phase")
-if CI_TEST_PHASE not in ["ALL", "BUILD"]:
- CI_TEST_PHASE = "ALL"
-
-
-def get_current_script_path():
- """Returns the absolute path containing this script."""
- try:
- this_file = __file__
- except NameError:
- this_file = sys.argv[0]
- this_file = os.path.abspath(this_file)
- return os.path.dirname(this_file)
-
-
-def is_snapshot_build():
- """
- Returns True if project needs to be built with --snapshot-build
-
- This is true if the version found in .cmake.conf is not a
- pre-release version (no alphas, betas).
-
- This eliminates the need to remove the --snapshot-build option
- on a per-release branch basis (less things to remember to do
- for a release).
- """
- setup_script_dir = get_current_script_path()
- pyside_project_dir = os.path.join(setup_script_dir, "sources", "pyside6")
-
- d = parse_cmake_conf_assignments_by_key(pyside_project_dir)
- release_version_type = d.get("pyside_PRE_RELEASE_VERSION_TYPE")
- pre_release_version = d.get("pyside_PRE_RELEASE_VERSION")
- if pre_release_version and release_version_type:
- return True
- return False
-
-
-def call_setup(python_ver, phase):
- print("call_setup")
- print("python_ver", python_ver)
- print("phase", phase)
- _pExe, _env, env_pip, env_python = get_qtci_virtualEnv(
- python_ver, CI_HOST_OS, CI_HOST_ARCH, CI_TARGET_ARCH
- )
-
- if phase in ["BUILD"]:
- remove_tree(_env, True)
- # Pinning the virtualenv before creating one
- # Use pip3 if possible while pip seems to install the virtualenv to wrong dir in some OS
- python3 = "python3"
- if sys.platform == "win32":
- python3 = os.path.join(os.getenv("PYTHON3_PATH"), "python.exe")
- run_instruction(
- [python3, "-m", "pip", "install", "--user", "virtualenv==20.7.2"],
- "Failed to pin virtualenv",
- )
- # installing to user base might not be in PATH by default.
- env_path = os.path.join(site.USER_BASE, "bin")
- v_env = os.path.join(env_path, "virtualenv")
- if sys.platform == "win32":
- env_path = os.path.join(site.USER_BASE, "Scripts")
- v_env = os.path.join(env_path, "virtualenv.exe")
- try:
- run_instruction([str(v_env), "--version"], "Using default virtualenv")
- except Exception as e:
- log.info("Failed to use the default virtualenv")
- log.info(f"{type(e).__name__}: {e}")
- v_env = "virtualenv"
- run_instruction([str(v_env), "-p", str(_pExe), str(_env)], "Failed to create virtualenv")
- # When the 'python_ver' variable is empty, we are using Python 2
- # Pip is always upgraded when CI template is provisioned,
- # upgrading it in later phase may cause perm issue
- run_instruction(
- [str(env_pip), "install", "-r", "requirements.txt"], "Failed to install dependencies"
- )
+from build_scripts.utils import expand_clang_variables
+from coin.instructions_utils import (CI, call_setup, get_architecture,
+ get_python_version, remove_variables)
- cmd = [env_python, "-u", "setup.py"]
- if phase in ["BUILD"]:
- cmd += ["build", "--standalone", "--unity"]
-
- cmd += ["--build-tests", "--log-level=verbose"]
-
- if CI_TARGET_ARCH == "X86_64-ARM64":
- cmd += ["--macos-arch='x86_64;arm64'"]
-
- if CI_USE_SCCACHE:
- cmd += [f"--compiler-launcher={CI_USE_SCCACHE}"]
-
- cmd += ["--limited-api=yes"]
-
- if is_snapshot_build():
- cmd += ["--snapshot-build"]
-
- qtpaths_path = get_ci_qtpaths_path(CI_ENV_INSTALL_DIR, CI_HOST_OS)
- cmd.append(qtpaths_path)
+if __name__ == "__main__":
- # Due to certain older CMake versions generating very long paths
- # (at least with CMake 3.6.2) when using the export() function,
- # pass the shorter paths option on Windows so we don't hit
- # the path character length limit (260).
- if CI_HOST_OS == "Windows":
- cmd += ["--shorter-paths"]
+ ci = CI()
+ log.setLevel(logging.INFO)
+ # Remove some environment variables that impact cmake
+ arch = get_architecture(ci)
+ expand_clang_variables(arch)
- cmd += ["--package-timestamp=" + CI_INTEGRATION_ID]
+ remove_variables(["CC", "CXX"])
- env = os.environ
- run_instruction(cmd, "Failed to run setup.py for build", initial_env=env)
+ python_ver = get_python_version(ci)
+ if ci.TEST_PHASE in ["ALL", "BUILD"]:
+ call_setup(python_ver, ci, "BUILD", log)
-if __name__ == "__main__":
- # Remove some environment variables that impact cmake
- arch = "32" if CI_TARGET_ARCH == "X86" else "64"
- expand_clang_variables(arch)
- for env_var in ["CC", "CXX"]:
- if os.environ.get(env_var):
- del os.environ[env_var]
- python_ver = "3"
- if CI_TARGET_OS in ["Linux"] and CI_HOST_ARCH != "aarch64":
- python_ver = "3.11"
- wheel_package_dir = "qfpa-p3.6"
- if CI_TARGET_OS in ["Windows"]:
- python_ver = "3.10.0"
- if CI_TEST_PHASE in ["ALL", "BUILD"]:
- call_setup(python_ver, "BUILD")
# Until CI has a feature to set more dynamic signing dir, make sure it actually exist
if os.environ.get("QTEST_ENVIRONMENT") == "ci" and sys.platform == "win32":
signing_dir = str(os.environ.get("PYSIDE_SIGNING_DIR"))
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-import os
+from __future__ import annotations
+
import logging
-import site
-import sys
+import os
from build_scripts.log import log
-from build_scripts.options import has_option, option_value
-from build_scripts.utils import (expand_clang_variables, get_ci_qmake_path,
- get_qtci_virtualEnv, remove_tree, run_instruction)
-
-log.setLevel(logging.INFO)
-
-# Values must match COIN thrift
-CI_HOST_OS = option_value("os")
-CI_TARGET_OS = option_value("targetOs")
-CI_HOST_ARCH = option_value("hostArch")
-CI_TARGET_ARCH = option_value("targetArch")
-CI_HOST_OS_VER = option_value("osVer")
-CI_ENV_INSTALL_DIR = option_value("instdir")
-CI_ENV_AGENT_DIR = option_value("agentdir") or "."
-CI_COMPILER = option_value("compiler")
-CI_FEATURES = []
-_ci_features = option_value("features")
-if _ci_features is not None:
- for f in _ci_features.split(', '):
- CI_FEATURES.append(f)
-CI_RELEASE_CONF = has_option("packaging")
-
-
-def call_testrunner(python_ver, buildnro):
- _pExe, _env, env_pip, env_python = get_qtci_virtualEnv(python_ver, CI_HOST_OS, CI_HOST_ARCH,
- CI_TARGET_ARCH)
- remove_tree(_env, True)
- # Pinning the virtualenv before creating one
- # Use pip3 if possible while pip seems to install the virtualenv to wrong dir in some OS
- python3 = "python3"
- if sys.platform == "win32":
- python3 = os.path.join(os.getenv("PYTHON3_PATH"), "python.exe")
-
- # we shouldn't install anything outside of virtualenv, while m1 is not virtualized yet
- if CI_HOST_OS == "MacOS" and CI_HOST_ARCH == "ARM64":
- v_env = "virtualenv"
- run_instruction([str(v_env), "-p", str(_pExe), str(_env)], "Failed to create virtualenv")
- run_instruction([env_pip, "install", "-r", "requirements.txt"],
- "Failed to install dependencies")
- else:
- run_instruction([python3, "-m", "pip", "install", "--user", "virtualenv==20.7.2"],
- "Failed to pin virtualenv")
- # installing to user base might not be in PATH by default.
- env_path = os.path.join(site.USER_BASE, "bin")
- v_env = os.path.join(env_path, "virtualenv")
- if sys.platform == "win32":
- env_path = os.path.join(site.USER_BASE, "Scripts")
- v_env = os.path.join(env_path, "virtualenv.exe")
- try:
- run_instruction([str(v_env), "--version"], "Using default virtualenv")
- except Exception as e:
- log.info("Failed to use the default virtualenv")
- log.info(f"{type(e).__name__}: {e}")
- v_env = "virtualenv"
- run_instruction([str(v_env), "-p", str(_pExe), str(_env)], "Failed to create virtualenv")
- # When the 'python_ver' variable is empty, we are using Python 2
- # Pip is always upgraded when CI template is provisioned,
- # upgrading it in later phase may cause perm issue
- run_instruction([env_pip, "install", "-r", "requirements.txt"],
- "Failed to install dependencies")
- # Install distro to replace missing platform.linux_distribution() in python3.8
- run_instruction([env_pip, "install", "distro"], "Failed to install distro")
+from build_scripts.utils import expand_clang_variables
+from coin.instructions_utils import (CI, call_setup, get_architecture,
+ get_python_version, remove_variables)
- cmd = [env_python, "testrunner.py", "test", "--blacklist", "build_history/blacklist.txt",
- f"--buildno={buildnro}"]
- run_instruction(cmd, "Failed to run testrunner.py")
-
- qmake_path = get_ci_qmake_path(CI_ENV_INSTALL_DIR, CI_HOST_OS)
-
- # Try to install built wheels, and build some buildable examples.
- if CI_RELEASE_CONF:
- wheel_tester_path = os.path.join("testing", "wheel_tester.py")
- # Run the test for the new set of wheels
- cmd = [env_python, wheel_tester_path, qmake_path, "--wheels-dir=dist", "--new"]
- run_instruction(cmd, "Error while running wheel_tester.py on new wheels")
-
-
-def run_test_instructions():
+if __name__ == "__main__":
+ ci = CI()
+ log.setLevel(logging.INFO)
# Remove some environment variables that impact cmake
- arch = '32' if CI_TARGET_ARCH == 'X86' else '64'
+ arch = get_architecture(ci)
expand_clang_variables(arch)
- for env_var in ['CC', 'CXX']:
- if os.environ.get(env_var):
- del os.environ[env_var]
- os.chdir(CI_ENV_AGENT_DIR)
- testRun = 0
+ remove_variables(["CC", "CXX"])
- # In win machines, there are additional python versions to test with
- if CI_HOST_OS == "Windows":
- call_testrunner("3.10.0", str(testRun))
- elif CI_HOST_OS == "Linux":
- call_testrunner("3.11", str(testRun))
- else:
- call_testrunner("3", str(testRun))
+ python_ver = get_python_version(ci)
+ os.chdir(ci.ENV_AGENT_DIR)
+ testRun = 0
-if __name__ == "__main__":
- run_test_instructions()
+ call_setup(python_ver, ci, "TEST", log)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import os
import platform
from dataclasses import dataclass
from pathlib import Path
from shutil import copy, rmtree, copytree
-from typing import List, Optional, Tuple
import build # type: ignore
import pyproject_hooks
@dataclass
class SetupData:
name: str
- version: str
+ version: tuple[str, str]
description: str
readme: str
- console_scripts: List[str]
+ console_scripts: list[str]
-def get_version_from_package(name: str, package_path: Path) -> str:
+def get_version_from_package(name: str, package_path: Path) -> tuple[str, str]:
# Get version from the already configured '__init__.py' file
version = ""
with open(package_path / name / "__init__.py") as f:
return version, f"{name}.__init__.__version__"
-def create_module_plugin_json(wheel_name: str, data: List[ModuleData], package_path: Path):
+def create_module_plugin_json(wheel_name: str, data: list[ModuleData], package_path: Path):
all_plugins = {}
for module in data:
json.dump(all_plugins, fp, indent=4)
-def get_manifest(wheel_name: str, data: List[ModuleData], package_path: Path) -> str:
+def get_manifest(wheel_name: str, data: list[ModuleData], package_path: Path) -> str:
lines = []
for module in data:
module_name = config_py.name[:-3]
_spec = importlib.util.spec_from_file_location(f"{module_name}", config_py)
+ if _spec is None:
+ raise RuntimeError(f"Unable to create ModuleSpec from {str(config_py)}")
_module = importlib.util.module_from_spec(_spec)
+ if _spec.loader is None:
+ raise RuntimeError(f"ModuleSpec for {module_name} has no valid loader.")
_spec.loader.exec_module(module=_module)
target = _module.__qt_macos_min_deployment_target__
with open(artifacts / "pyproject.toml.base") as f:
content = (
f.read()
- .replace("PROJECT_NAME", f'"{setup.name}"')
- .replace("PROJECT_VERSION", f'"{setup.version[1]}"')
- .replace("PROJECT_DESCRIPTION", f'"{setup.description}"')
- .replace("PROJECT_README", f'"{setup.readme}"')
- .replace("PROJECT_TAG", f'"{_tag}"')
- .replace("PROJECT_SCRIPTS", _console_scripts)
- .replace("PROJECT_DEPENDENCIES", f"{_dependencies}")
+ .replace('"PROJECT_NAME"', f'"{setup.name}"')
+ .replace('"PROJECT_VERSION"', f'"{setup.version[1]}"')
+ .replace('"PROJECT_DESCRIPTION"', f'"{setup.description}"')
+ .replace('"PROJECT_README"', f'"{setup.readme}"')
+ .replace('"PROJECT_TAG"', f'"{_tag}"')
+ .replace('# PROJECT_SCRIPTS', _console_scripts)
+ .replace('"PROJECT_DEPENDENCIES"', f"{_dependencies}")
)
return content
return content
-def wheel_shiboken_generator(package_path: Path) -> Tuple[SetupData, None]:
+def wheel_shiboken_generator(package_path: Path) -> tuple[SetupData, None]:
setup = SetupData(
name="shiboken6_generator",
version=get_version_from_package("shiboken6_generator", package_path),
return setup, None
-def wheel_shiboken_module(package_path: Path) -> Tuple[SetupData, None]:
+def wheel_shiboken_module(package_path: Path) -> tuple[SetupData, None]:
setup = SetupData(
name="shiboken6",
version=get_version_from_package("shiboken6", package_path),
return setup, None
-def wheel_pyside6_essentials(package_path: Path) -> Tuple[SetupData, List[ModuleData]]:
+def wheel_pyside6_essentials(package_path: Path) -> tuple[SetupData, list[ModuleData]]:
packaged_qt_tools_path = package_path / "PySide6"
set_pyside_package_path(packaged_qt_tools_path)
_pyside_tools = available_pyside_tools(packaged_qt_tools_path, package_for_wheels=True)
# replacing pyside6-android_deploy by pyside6-android-deploy for consistency
- # Also, the tool should not exist in any other platform than Linux
+ # Also, the tool should not exist in any other platform than Linux and macOS
_console_scripts = []
- if ("android_deploy" in _pyside_tools) and sys.platform.startswith("linux"):
+ if ("android_deploy" in _pyside_tools) and sys.platform in ("linux", "darwin"):
_console_scripts = ['pyside6-android-deploy = "PySide6.scripts.pyside_tool:android_deploy"']
_pyside_tools.remove("android_deploy")
return setup, data
-def wheel_pyside6_addons(package_path: Path) -> Tuple[SetupData, List[ModuleData]]:
+def wheel_pyside6_addons(package_path: Path) -> tuple[SetupData, list[ModuleData]]:
setup = SetupData(
name="PySide6_Addons",
version=get_version_from_package("PySide6", package_path), # we use 'PySide6' here
return setup, data
-def wheel_pyside6(package_path: Path) -> Tuple[SetupData, Optional[List[ModuleData]]]:
+def wheel_pyside6(package_path: Path) -> tuple[SetupData, list[ModuleData] | None]:
setup = SetupData(
name="PySide6",
version=get_version_from_package("PySide6", package_path),
return setup, None
-def wheel_pyside6_examples(package_path: Path) -> Tuple[SetupData, Optional[List[ModuleData]]]:
+def wheel_pyside6_examples(package_path: Path) -> tuple[SetupData, list[ModuleData] | None]:
setup = SetupData(
name="PySide6_Examples",
version=get_version_from_package("PySide6", package_path),
help="The directory where PySide was build (must contain a "
"'package_for_wheels' folder"
)
+ parser.add_argument("--no-examples", "-e", action="store_true",
+ help="Do not build examples wheel")
options = parser.parse_args()
# Sanity check between the available modules,
"PySide6_Essentials": wheel_pyside6_essentials,
"PySide6_Addons": wheel_pyside6_addons,
"PySide6": wheel_pyside6,
- "PySide6_Examples": wheel_pyside6_examples,
}
+ if not options.no_examples:
+ wheels["PySide6_Examples"] = wheel_pyside6_examples
for name, wheel_info in wheels.items():
print("-- Copy configuration files to create the wheel")
if name == "PySide6_Examples":
copy_examples_for_wheel(package_path)
- _files: List[Path] = base_files + [Path(setup.readme)]
+ _files: list[Path] = base_files + [Path(setup.readme)]
for fname in _files:
copy(fname, package_path)
--- /dev/null
+Qt for Python 6.7.3 is a bug-fix release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* PySide6 *
+****************************************************************************
+
+ - A --flatpak option has been added to setup.py, enabling a flatpak build
+ of Qt for Python.
+ - [PYSIDE-769] QtAsyncio: The application argument has been removed
+ from the loop policy.
+ - [PYSIDE-1612] Deployment: Nuitka has been updated to 2.3.7. Scanning for
+ QML dependencies has been fixed to skip some directories.
+ - [PYSIDE-1612] Android Cross Compilation: INSTSONAME has been added.
+ - [PYSIDE-1877] Properties of type QAbstractItemModel can now be used in QML.
+ - [PYSIDE-2192] PySide Qt Gui applications can now be used in interactive
+ mode, for example notebooks.
+ - [PYSIDE-2517] Type hints: The signatures of QObject.findChild()/
+ findChildren() have been improved to reflect the type passed
+ in.
+ - [PYSIDE-2622] Deployment: Nuitka --standalone mode is now supported.
+ - [PYSIDE-2656] QtMultimedia on macOS has been fixed.
+ - [PYSIDE-2702] An option to force processing system headers has been added
+ for cases where Qt is installed into the system.
+ - [PYSIDE-2752] Type hints: A syntax error caused by empty Enums has been
+ fixed.
+ - [PYSIDE-2766] Android Deployment: pyside6-android-deploy now works macOS,
+ too.
+ - [PYSIDE-2785] Deployment: 'dist-packages' is now skipped similar
+ to 'site-packages' when scanning for QML dependencies.
+ - [PYSIDE-2788] Type hints: The signature of QFormLayout.getLayoutPosition()
+ has been fixed.
+ - [PYSIDE-2789] numpy 2.0 is now supported.
+ - [PYSIDE-2790] QtAsyncio: cancel count and uncancel() have been added.
+ - [PYSIDE-2796] A potential crash in currentOpcode_Is_CallMethNoArgs()
+ has been fixed.
+ - [PYSIDE-2799] QtAsyncio: A hang when an exception occurs inside a
+ TaskGroup body has been fixed.
+ - [PYSIDE-2803] Desktop Deployment: Overflows of command lines on Windows
+ have been fixed.
+ - [PYSIDE-2806] Desktop Deployment: The application name has been fixed.
+ - [PYSIDE-2814] Deployment: Arguments with spaces can now be used for
+ "extra_args" due to using shlex for splitting the command
+ line arguments.
+ - [PYSIDE-2819] The correct libclang is now used for arm64.
+ - [PYSIDE-2825] QtWebView has been added.
+ - [PYSIDE-2828] Documentation: The .qrc tutorial has been updated.
+ - [PYSIDE-2833] The QML tutorials have been updated.
+ - [PYSIDE-2834] QDir.entry(Info)List(QDir.Filter, QDir.SortFlags)
+ has been fixed to work with Python 3.11 and later.
+ - [PYSIDE-2836] PySide6/__init__.py now has a static list of modules,
+ enabling code checkers to work.
+ - [PYSIDE-2870] A crash when using QStateMachine.postEvent() has been
+ fixed.
+
+****************************************************************************
+* Shiboken6 *
+****************************************************************************
+
+- [PYSIDE-2834] Enumerations have been excluded from argument type checks
+ for sequences. This addresses a problem showing in Python
+ 3.11 causing the wrong function overloads to be used.
+- [PYSIDE-2780] A potential refcounting bug in Lazy loading has been fixed.
--- /dev/null
+Qt for Python 6.8.0 is a minor release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* PySide6 *
+****************************************************************************
+
+ - [PYSIDE-769] QtAsyncio: The application argument has been removed
+ from the loop policy.
+ - [PYSIDE-2221] The code for signal connections for Python callables not
+ targeting a QMetaMethod has be reimplemented to simplify
+ code and prepare for removal of the GIL.
+ - [PYSIDE-2404] Lazy loading has been extended to smart pointers
+ and nested types.
+ - [PYSIDE-2620] pyside6-svgtoqml has been added.
+ - [PYSIDE-2701] The size of the generated code has been reduced.
+ - [PYSIDE-2701] Python formats with "#" in it are now supported.
+ - [PYSIDE-2711] An undesired invalidation of QObjects/QWidgets after
+ calling parent(Widget)() has been fixed.
+ - [PYSIDE-2766] Android wheels are now provided.
+ - [PYSIDE-2786] Modern typing syntax is now used in examples and QtAsyncio
+ code, enabled by a future import for Python 3.9, too.
+ - [PYSIDE-2805] An overload taking a slot connected to QMessageBox.accepted
+ has been added to QMessageBox.open().
+ - [PYSIDE-2810] The number of QByteArray conversions in libpyside's
+ signal handling has been reduced.
+ - [PYSIDE-2820] Opaque containers of type QList can now be used for initial
+ properties in QML.
+ - [PYSIDE-2803] Deployment: An issue in QML import scanning causing
+ overly long command lines has been fixed.
+ - [PYSIDE-2837] The documentation has been restructured and updated.
+ - [PYSIDE-2859] QModelRoleDataSpan for implementing
+ QAbstractItemModel.multiData() has been added.
+ - [PYSIDE-2840] Enumeration properties for Qt Designer custom widgets
+ have been fixed.
+ - [PYSIDE-2845] QSemaphore.tryAcquire() has been fixed to work with
+ the snake case feature. The existing C++ function
+ try_acquire() has been removed.
+ - [PYSIDE-2846] Type hints: Many issues in the generated stub files
+ have been fixed to enable checking with mypy.
+
+****************************************************************************
+* Shiboken6 *
+****************************************************************************
+
+ - [PYSIDE-2792] Potential mismatches caused by nested classes
+ when looking up converters by name have been fixed.
+ - [PYSIDE-1735] The generated code for enumerations has been refactored
+ to consider underlying types.
--- /dev/null
+Qt for Python 6.8.1 is a bug-fix release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* PySide6 *
+****************************************************************************
+
+ - [PYSIDE-1106] Documentation: Inner classes are now shown nested
+ into their outer classes for referencing to work correctly.
+ - [PYSIDE-2689] Documentation: A workaround has been made to fix the search
+ context of class pages.
+ - [PYSIDE-1612] Deployment: The bundling of QML files has been improved
+ by using nuitka's 'include-data-dir' command line option.
+ - [PYSIDE-2206] The OSM-buildings demo (maps part) and
+ the zoomlinechart example have been added.
+ - [PYSIDE-2211] Type hints: Errors resulting from inconsistencies
+ between binary operators and their corresponding
+ in-place operators have been fixed.
+ - [PYSIDE-2751] Support for Python 3.13 has been added.
+ - [PYSIDE-2825] Missing libraries required for QtWebView have been added.
+ - [PYSIDE-2846] Type hints: mypy support has been greatly improved
+ and the .pyi files are now tested.
+ - [PYSIDE-2884] Type hints: typing.Callable has been fixed to include the
+ parameters.
+ - [PYSIDE-2886] Type hints: Multiple signatures and Union expressions
+ have been fixed.
+ - [PYSIDE-2891] An issue affecting 32bit builds has been fixed.
+ - [PYSIDE-2893] The installation of the module doc snippet files has
+ been fixed.
+ - [PYSIDE-2895] PySide's __init__.py no longer contains
+ build-directory-dependent code and PySide6.__all__ has been
+ fixed to again work.
+ - [PYSIDE-2901] Documentation: Tutorials and reference pages
+ related to Signal and Slots have been improved.
+ - [PYSIDE-2903] Type hints: Duplicated occurrences of '#' in the pyi type
+ specifications causing errors have been fixed.
+ - [PYSIDE-2910] Deployment: Bundling of the FluentWinUI3
+ style for Qt Quick Controls has been fixed.
+ - [PYSIDE-2913] QWebEngineDesktopMediaRequest has been added.
+ - [PYSIDE-2915] Type hints: Rich compare functions are now supported.
+ - [PYSIDE-2917] numpy version 2 is now used Python 3.10+.
--- /dev/null
+Qt for Python 6.8.1.1 is a bug-fix release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* PySide6 *
+****************************************************************************
+
+ - [PYSIDE-2964] Added WebView to macOS wheels.
--- /dev/null
+Qt for Python 6.8.2 is a bug-fix release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* PySide6 *
+****************************************************************************
+
+ - [PYSIDE-487] Some missing bindings were added.
+ - [PYSIDE-841] The simple bar graph (QtGraphs) and zoomlinechart (QtCharts)
+ examples have been added.
+ - [PYSIDE-1106] Documentation: A number of missing documentation pages has
+ been added. The module pages were updated and improved, now
+ showing the QML types more prominently. Parameter names and
+ documentation of global and/or added functions have been
+ added.
+ - [PYSIDE-1612] Deployment: Development folders are no longer included in the
+ deployed executable. A bug parsing "from . import x" has been
+ fixed. .webp files are now supported. Nuitka has been upgraded
+ to 2.5.1. Non-existent plugins are now disregarded.
+ - [PYSIDE-1612] Android Deployment: The Android NDK is now automatically
+ downloaded.
+ - [PYSIDE-1735] Examples and tests have been changed to use fully qualified
+ enumerations.
+ - [PYSIDE-2624] Type hints: Positional parameters are now used according to
+ PEP 570.
+ - [PYSIDE-2846] Type hints: Properties in constructors are now supported
+ (including snake_case). Empty enums have been fixed for
+ mypy 1.14.
+ - [PYSIDE-2846] A crash related to inner classes in Qt3DRender has been fixed.
+ - [PYSIDE-2936] A crash using QDBusUnixFileDescriptor in
+ QDBusAbstractInterface::call() has been fixed.
+ - [PYSIDE-2939] Type hints: The heuristic for ambiguous signature removal
+ has been fixed.
+ - [PYSIDE-2941] Type hints: Some optional return annotations have been fixed.
+ - [PYSIDE-2946] A crash registering smart pointer types conversions of class
+ hierarchies has been fixed.
+
+****************************************************************************
+* Shiboken6 *
+****************************************************************************
+
+ - [PYSIDE-2701] The generated code has been streamlined.
--- /dev/null
+Qt for Python 6.8.2.1 is a bug-fix release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* PySide6 *
+****************************************************************************
+
+ - [PYSIDE-3000] A bug in 6.8.2 causing the MSVC runtime libraries no longer
+ being shipped has been fixed.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the qt3d/simple-cpp example from Qt v5.x"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import (Qt, QObject, Signal, Slot)
from PySide6.QtGui import (QColor, QFont, QPalette)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import (Qt, QEvent, QObject, QTimer, Signal, Slot)
from PySide6.QtGui import (QColor, QFont, QPalette)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Qt
from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import (Qt, QEvent, QObject, Signal, Slot)
from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 Active Qt Viewer example"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QPoint, Qt, Slot
from PySide6.QtGui import QColor
self.host_mode_state_changed(self._local_device.hostMode())
# add context menu for devices to be able to pair device
- self._ui.list.setContextMenuPolicy(Qt.CustomContextMenu)
+ self._ui.list.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self._ui.list.customContextMenuRequested.connect(self.display_pairing_menu)
self._local_device.pairingFinished.connect(self.pairing_done)
if not items:
item = QListWidgetItem(label)
pairing_status = self._local_device.pairingStatus(info.address())
- if (pairing_status == QBluetoothLocalDevice.Paired
- or pairing_status == QBluetoothLocalDevice.AuthorizedPaired):
- item.setForeground(QColor(Qt.green))
+ if (pairing_status == QBluetoothLocalDevice.Pairing.Paired
+ or pairing_status == QBluetoothLocalDevice.Pairing.AuthorizedPaired):
+ item.setForeground(QColor(Qt.GlobalColor.green))
else:
- item.setForeground(QColor(Qt.black))
+ item.setForeground(QColor(Qt.GlobalColor.black))
self._ui.list.addItem(item)
@Slot()
@Slot(bool)
def on_discoverable_clicked(self, clicked):
if clicked:
- self._local_device.setHostMode(QBluetoothLocalDevice.HostDiscoverable)
+ self._local_device.setHostMode(QBluetoothLocalDevice.HostMode.HostDiscoverable)
else:
- self._local_device.setHostMode(QBluetoothLocalDevice.HostConnectable)
+ self._local_device.setHostMode(QBluetoothLocalDevice.HostMode.HostConnectable)
@Slot(bool)
def on_power_clicked(self, clicked):
@Slot("QBluetoothLocalDevice::HostMode")
def host_mode_state_changed(self, mode):
- self._ui.power.setChecked(mode != QBluetoothLocalDevice.HostPoweredOff)
- self._ui.discoverable.setChecked(mode == QBluetoothLocalDevice.HostDiscoverable)
+ self._ui.power.setChecked(mode != QBluetoothLocalDevice.HostMode.HostPoweredOff)
+ self._ui.discoverable.setChecked(mode == QBluetoothLocalDevice.HostMode.HostDiscoverable)
- on = mode != QBluetoothLocalDevice.HostPoweredOff
+ on = mode != QBluetoothLocalDevice.HostMode.HostPoweredOff
self._ui.scan.setEnabled(on)
self._ui.discoverable.setEnabled(on)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the bluetooth/btscanner example from Qt v6.x"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Slot
from PySide6.QtWidgets import QDialog
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
- <set>QDialogButtonBox::Close</set>
+ <set>QDialogButtonBox::StandardButton::Close</set>
</property>
</widget>
</item>
################################################################################
## Form generated from reading UI file 'device.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
################################################################################
## Form generated from reading UI file 'service.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.buttonBox = QDialogButtonBox(ServiceDiscovery)
self.buttonBox.setObjectName(u"buttonBox")
- self.buttonBox.setStandardButtons(QDialogButtonBox.Close)
+ self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Close)
self.verticalLayout.addWidget(self.buttonBox)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property, Signal, Slot
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtBluetooth import (QBluetoothDeviceDiscoveryAgent,
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import struct
self.m_stop = QDateTime()
self.m_measurements = []
- self.m_addressType = QLowEnergyController.PublicAddress
+ self.m_addressType = QLowEnergyController.RemoteAddressType.PublicAddress
self.m_demoTimer = QTimer()
@Property(int)
def addressType(self):
- if self.m_addressType == QLowEnergyController.RandomAddress:
+ if self.m_addressType == QLowEnergyController.RemoteAddressType.RandomAddress:
return DeviceHandler.AddressType.RANDOM_ADDRESS
return DeviceHandler.AddressType.PUBLIC_ADDRESS
@addressType.setter
def addressType(self, type):
if type == DeviceHandler.AddressType.PUBLIC_ADDRESS:
- self.m_addressType = QLowEnergyController.PublicAddress
+ self.m_addressType = QLowEnergyController.RemoteAddressType.PublicAddress
elif type == DeviceHandler.AddressType.RANDOM_ADDRESS:
- self.m_addressType = QLowEnergyController.RandomAddress
+ self.m_addressType = QLowEnergyController.RemoteAddressType.RandomAddress
@Slot(QLowEnergyController.Error)
def controllerErrorOccurred(self, device):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the bluetooth/heartrate-game example from Qt v6.x"""
if not engine.rootObjects():
sys.exit(-1)
- ex = QCoreApplication.exec()
+ exit_code = QCoreApplication.exec()
del engine
- sys.exit(ex)
+ sys.exit(exit_code)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the bluetooth/heartrate-server example from Qt v6.x"""
#! [Advertising Data]
advertising_data = QLowEnergyAdvertisingData()
- advertising_data.setDiscoverability(QLowEnergyAdvertisingData.DiscoverabilityGeneral)
+ advertising_data.setDiscoverability(QLowEnergyAdvertisingData.Discoverability.DiscoverabilityGeneral) # noqa: E501
advertising_data.setIncludePowerLevel(True)
advertising_data.setLocalName("HeartRateServer")
advertising_data.setServices([QBluetoothUuid.ServiceClassUuid.HeartRate])
char_data = QLowEnergyCharacteristicData()
char_data.setUuid(QBluetoothUuid.CharacteristicType.HeartRateMeasurement)
char_data.setValue(QByteArray(2, 0))
- char_data.setProperties(QLowEnergyCharacteristic.Notify)
+ char_data.setProperties(QLowEnergyCharacteristic.PropertyType.Notify)
client_config = QLowEnergyDescriptorData(
QBluetoothUuid.DescriptorType.ClientCharacteristicConfiguration, QByteArray(2, 0))
char_data.addDescriptor(client_config)
service_data = QLowEnergyServiceData()
- service_data.setType(QLowEnergyServiceData.ServiceTypePrimary)
+ service_data.setType(QLowEnergyServiceData.ServiceType.ServiceTypePrimary)
service_data.setUuid(QBluetoothUuid.ServiceClassUuid.HeartRate)
service_data.addCharacteristic(char_data)
#! [Service Data]
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property, Signal
from PySide6.QtBluetooth import QLowEnergyCharacteristic, QBluetoothUuid
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import warnings
from PySide6.QtBluetooth import (QBluetoothDeviceDiscoveryAgent, QLowEnergyController,
QBluetoothDeviceInfo, QBluetoothUuid, QLowEnergyService)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the bluetooth/lowenergyscanner example from Qt v6.x"""
if not engine.rootObjects():
sys.exit(-1)
- ex = QCoreApplication.exec()
+ exit_code = QCoreApplication.exec()
del engine
- sys.exit(ex)
+ sys.exit(exit_code)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property, Signal
from PySide6.QtBluetooth import QLowEnergyService
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the areachart example from Qt v6.x"""
self.chart.addSeries(self.series)
self.chart.setTitle("Simple areachart example")
self.chart.createDefaultAxes()
- self.chart.axes(Qt.Horizontal)[0].setRange(0, 20)
+ self.chart.axes(Qt.Orientation.Horizontal)[0].setRange(0, 20)
self.chart.axes(Qt.Vertical)[0].setRange(0, 10)
self._chart_view = QChartView(self.chart)
- self._chart_view.setRenderHint(QPainter.Antialiasing)
+ self._chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
self.setCentralWidget(self._chart_view)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the charts/audio example from Qt v5.x"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the linechart example from Qt v6.x"""
self.chart.legend().setAlignment(Qt.AlignBottom)
self._chart_view = QChartView(self.chart)
- self._chart_view.setRenderHint(QPainter.Antialiasing)
+ self._chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
self.setCentralWidget(self._chart_view)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Callout example from Qt v5.x"""
self._chart.createDefaultAxes()
self._chart.setAcceptHoverEvents(True)
- self.setRenderHint(QPainter.Antialiasing)
+ self.setRenderHint(QPainter.RenderHint.Antialiasing)
self.scene().addItem(self._chart)
self._coordX = QGraphicsSimpleTextItem(self._chart)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Chart Themes example from Qt v5.x"""
lower_series = upper_series
chart.createDefaultAxes()
- axis_x = chart.axes(Qt.Horizontal)[0]
+ axis_x = chart.axes(Qt.Orientation.Horizontal)[0]
axis_x.setRange(0, self.value_count - 1)
axis_y = chart.axes(Qt.Vertical)[0]
axis_y.setRange(0, self.value_max)
chart.addSeries(series)
chart.createDefaultAxes()
- axis_x = chart.axes(Qt.Horizontal)[0]
+ axis_x = chart.axes(Qt.Orientation.Horizontal)[0]
axis_x.setRange(0, self.value_max)
axis_y = chart.axes(Qt.Vertical)[0]
axis_y.setRange(0, self.value_count)
chart.addSeries(series)
chart.createDefaultAxes()
- axis_x = chart.axes(Qt.Horizontal)[0]
+ axis_x = chart.axes(Qt.Orientation.Horizontal)[0]
axis_x.setRange(0, self.value_max)
axis_y = chart.axes(Qt.Vertical)[0]
axis_y.setRange(0, self.value_count)
chart.addSeries(series)
chart.createDefaultAxes()
- axis_x = chart.axes(Qt.Horizontal)[0]
+ axis_x = chart.axes(Qt.Orientation.Horizontal)[0]
axis_x.setRange(0, self.value_max)
axis_y = chart.axes(Qt.Vertical)[0]
axis_y.setRange(0, self.value_count)
# Update antialiasing
checked = self.ui.antialiasCheckBox.isChecked()
for chart in self.charts:
- chart.setRenderHint(QPainter.Antialiasing, checked)
+ chart.setRenderHint(QPainter.RenderHint.Antialiasing, checked)
# Update animation options
idx = self.ui.animatedComboBox.currentIndex()
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<signal>currentIndexChanged(int)</signal>
<receiver>ThemeWidgetForm</receiver>
<slot>update_ui()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>20</x>
+ <y>20</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>20</x>
+ <y>20</y>
+ </hint>
+ </hints>
</connection>
<connection>
<sender>antialiasCheckBox</sender>
<signal>toggled(bool)</signal>
<receiver>ThemeWidgetForm</receiver>
<slot>update_ui()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>20</x>
+ <y>20</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>20</x>
+ <y>20</y>
+ </hint>
+ </hints>
</connection>
<connection>
<sender>legendComboBox</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>ThemeWidgetForm</receiver>
<slot>update_ui()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>20</x>
+ <y>20</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>20</x>
+ <y>20</y>
+ </hint>
+ </hints>
</connection>
<connection>
<sender>animatedComboBox</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>ThemeWidgetForm</receiver>
<slot>update_ui()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>20</x>
+ <y>20</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>20</x>
+ <y>20</y>
+ </hint>
+ </hints>
</connection>
</connections>
<slots>
################################################################################
## Form generated from reading UI file 'themewidget.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Donut Chart Breakdown example from Qt v5.x"""
window = QMainWindow()
chart_view = QChartView(donut_breakdown)
- chart_view.setRenderHint(QPainter.Antialiasing)
+ chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
window.setCentralWidget(chart_view)
available_geometry = window.screen().availableGeometry()
size = available_geometry.height() * 0.75
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import random
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Dynamic Spline example from Qt v5.x"""
import sys
chart = Chart()
chart.setTitle("Dynamic spline chart")
chart.legend().hide()
- chart.setAnimationOptions(QChart.AllAnimations)
+ chart.setAnimationOptions(QChart.AnimationOption.AllAnimations)
chart_view = QChartView(chart)
- chart_view.setRenderHint(QPainter.Antialiasing)
+ chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
window.setCentralWidget(chart_view)
window.resize(400, 300)
window.show()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Legend example from Qt v5.x"""
self.chart.legend().setVisible(True)
self.chart.legend().setAlignment(Qt.AlignBottom)
- self.chart_view.setRenderHint(QPainter.Antialiasing)
+ self.chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
def show_legend_spinbox(self):
self.legend_settings.setVisible(True)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the line/bar example from Qt v5.x"""
self.chart.legend().setAlignment(Qt.AlignBottom)
self._chart_view = QChartView(self.chart)
- self._chart_view.setRenderHint(QPainter.Antialiasing)
+ self._chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
self.setCentralWidget(self._chart_view)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the linechart example from Qt v5.x"""
self.chart.setTitle("Simple line chart example")
self._chart_view = QChartView(self.chart)
- self._chart_view.setRenderHint(QPainter.Antialiasing)
+ self._chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
self.setCentralWidget(self._chart_view)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Logarithmic Axis Example from Qt v5.x"""
self.series.attachAxis(self._axis_y)
self._chart_view = QChartView(self.chart)
- self._chart_view.setRenderHint(QPainter.Antialiasing)
+ self._chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
self.setCentralWidget(self._chart_view)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 Charts example: Simple memory usage viewer"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Model Data example from Qt v5.x"""
return self.column_count
def headerData(self, section, orientation, role):
- if role != Qt.DisplayRole:
+ if role != Qt.ItemDataRole.DisplayRole:
return None
- if orientation == Qt.Horizontal:
+ if orientation == Qt.Orientation.Horizontal:
if section % 2 == 0:
return "x"
else:
else:
return str(section + 1)
- def data(self, index, role=Qt.DisplayRole):
- if role == Qt.DisplayRole:
+ def data(self, index, role=Qt.ItemDataRole.DisplayRole):
+ if role == Qt.ItemDataRole.DisplayRole:
return self.input_data[index.row()][index.column()]
- elif role == Qt.EditRole:
+ elif role == Qt.ItemDataRole.EditRole:
return self.input_data[index.row()][index.column()]
- elif role == Qt.BackgroundRole:
+ elif role == Qt.ItemDataRole.BackgroundRole:
for color, rect in self.mapping.items():
if rect.contains(index.column(), index.row()):
return QColor(color)
return QColor(Qt.white)
return None
- def setData(self, index, value, role=Qt.EditRole):
- if index.isValid() and role == Qt.EditRole:
+ def setData(self, index, value, role=Qt.ItemDataRole.EditRole):
+ if index.isValid() and role == Qt.ItemDataRole.EditRole:
self.input_data[index.row()][index.column()] = float(value)
self.dataChanged.emit(index, index)
return True
self.chart.createDefaultAxes()
self.chart_view = QChartView(self.chart)
- self.chart_view.setRenderHint(QPainter.Antialiasing)
+ self.chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
self.chart_view.setMinimumSize(640, 480)
# create main layout
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Nested Donuts example from Qt v5.x"""
self.setMinimumSize(800, 600)
self.donuts = []
self.chart_view = QChartView()
- self.chart_view.setRenderHint(QPainter.Antialiasing)
+ self.chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
self.chart = self.chart_view.chart()
self.chart.legend().setVisible(False)
self.chart.setTitle("Nested donuts demo")
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Percent Bar Chart example from Qt v5.x"""
chart.legend().setAlignment(Qt.AlignBottom)
chart_view = QChartView(chart)
- chart_view.setRenderHint(QPainter.Antialiasing)
+ chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
self.setCentralWidget(chart_view)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Pie Chart Example from Qt v5.x"""
self.chart.legend().hide()
self._chart_view = QChartView(self.chart)
- self._chart_view.setRenderHint(QPainter.Antialiasing)
+ self._chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
self.setCentralWidget(self._chart_view)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Selected Point Configuration Example from Qt 6.5"""
from PySide6.QtCore import QPointF, Slot
from PySide6.QtWidgets import QMainWindow, QLineEdit, QLabel, QComboBox
from PySide6.QtWidgets import QCheckBox, QWidget, QGridLayout, QHBoxLayout
from PySide6.QtCharts import QLineSeries, QXYSeries, QChart, QChartView
-from typing import Union
PointConfig = QXYSeries.PointConfiguration
self._select_point(4)
@Slot(QPointF)
- def _select_point(self, point: Union[QPointF, int]):
+ def _select_point(self, point: QPointF | int):
try:
index = (self._series.points().index(point.toPoint()) if
isinstance(point, QPointF) else point)
More information about that can be found in the documentation for
`QtCharts.QXYSeries.pointLabelsFormat`_.
-.. _`QtCharts.QXYSeries.pointLabelsFormat`: https://doc.qt.io/qtforpython/PySide6/QtCharts/QXYSeries.html#PySide6.QtCharts.PySide6.QtCharts.QXYSeries.pointLabelsFormat
+.. _`QtCharts.QXYSeries.pointLabelsFormat`: https://doc.qt.io/qt-6/qxyseries.html#pointLabelsFormat-prop
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Light Markers Points Selection example from Qt v6.2"""
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Light Markers Points Selection example from Qt v6.2"""
import sys
chart.legend().setVisible(False)
chart_view = QChartView(chart)
- chart_view.setRenderHint(QPainter.Antialiasing)
+ chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
control_widget = QWidget(window)
control_layout = QGridLayout(control_widget)
@Slot(int)
def display_unselected_points(checkbox_state):
- if checkbox_state:
+ if checkbox_state == Qt.CheckState.Checked:
series.setLightMarker(
Utilities.get_point_representation(
Utilities.point_type(char_point_combobox.currentIndex()), marker_size))
show_unselected_points_label = QLabel("Display unselected points: ")
show_unselected_points_checkbox.setChecked(True)
- show_unselected_points_checkbox.stateChanged.connect(display_unselected_points)
+ show_unselected_points_checkbox.checkStateChanged.connect(display_unselected_points)
control_label = QLabel("Marker and Selection Controls")
control_label.setAlignment(Qt.AlignHCenter)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtGui import QImage, QPainter, QColor
from PySide6.QtCore import Qt
image = QImage(image_size, image_size, QImage.Format_RGB32)
painter = QPainter()
painter.begin(image)
- painter.setRenderHint(QPainter.Antialiasing)
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
painter.fillRect(0, 0, image_size, image_size, point_type[2])
painter.end()
return image
image.fill(QColor(0, 0, 0, 0))
painter = QPainter()
painter.begin(image)
- painter.setRenderHint(QPainter.Antialiasing)
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
painter.setBrush(point_type[2])
pen = painter.pen()
pen.setWidth(0)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the QML Polar Chart Example from Qt v5.x"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Temperature Records example from Qt v5.x"""
chart.legend().setAlignment(Qt.AlignBottom)
chart_view = QChartView(chart)
- chart_view.setRenderHint(QPainter.Antialiasing)
+ chart_view.setRenderHint(QPainter.RenderHint.Antialiasing)
self.setCentralWidget(chart_view)
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from PySide6.QtWidgets import QGesture, QGestureEvent
+from PySide6.QtCore import Qt, QEvent
+from PySide6.QtCharts import QChart
+
+
+class Chart(QChart):
+ def __init__(self,
+ ChartType=QChart.ChartType.ChartTypeCartesian,
+ QGraphicsItem=None,
+ WindowType=Qt.WindowFlags):
+ super().__init__()
+
+ self.grabGesture(Qt.PanGesture)
+ self.grabGesture(Qt.PinchGesture)
+
+ def sceneEvent(self, event: QEvent):
+
+ if event.type() == QEvent.Gesture:
+ return self.gestureEvent(event)
+
+ return super().sceneEvent(event)
+
+ def gestureEvent(self, event: QGestureEvent):
+
+ if gesture := event.gesture(Qt.PanGesture):
+ pan = gesture
+ self.scroll(-pan.delta().x(), pan.delta().y())
+
+ if gesture := event.gesture(Qt.PinchGesture):
+ pinch = gesture
+
+ if pinch.changeFlags() & QGesture.QPinchGesture.ScaleFactorChanged:
+ self.zoom(pinch.scaleFactor())
+
+ return True
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from PySide6.QtCharts import QChart, QChartView
+from PySide6.QtGui import QKeyEvent, QMouseEvent
+from PySide6.QtCore import QEvent, Qt
+from PySide6.QtWidgets import QGraphicsView
+
+
+class ChartView(QChartView):
+ def __init__(self, chart, parent=None):
+ super().__init__(chart, parent)
+
+ self.setRubberBand(QChartView.RectangleRubberBand)
+ self._isTouching = False
+
+ def viewPortEvent(self, event: QEvent):
+
+ if event.type() == QMouseEvent.TouchBegin:
+ self._isTouching = True
+
+ self.chart().setAnimationOptions(QChart.NoAnimation)
+
+ return super().viewPortEvent(event)
+
+ def mousePressEvent(self, event: QMouseEvent):
+
+ if self._isTouching:
+ return
+
+ return super().mousePressEvent(event)
+
+ def mouseMoveEvent(self, event: QMouseEvent):
+
+ if self._isTouching:
+ return
+
+ return super().mouseMoveEvent(event)
+
+ def mouseReleaseEvent(self, event: QMouseEvent):
+
+ if self._isTouching:
+ self._isTouching = False
+
+ self.chart().setAnimationOptions(QChart.SeriesAnimations)
+
+ return super().mouseReleaseEvent(event)
+
+ def keyPressEvent(self, event: QKeyEvent):
+
+ key = event.key()
+ if key == Qt.Key_Plus:
+ self.chart().zoomIn()
+
+ elif key == Qt.Key_Minus:
+ self.chart().zoomOut()
+
+ elif key == Qt.Key_Left:
+ self.chart().scroll(-10, 0)
+
+ elif key == Qt.Key_Right:
+ self.chart().scroll(10, 0)
+
+ elif key == Qt.Key_Up:
+ self.chart().scroll(0, 10)
+
+ elif key == Qt.Key_Down:
+ self.chart().scroll(0, -10)
+
+ else:
+ QGraphicsView.keyPressEvent(event)
--- /dev/null
+Zoom Line Chart Example
+=======================
+
+The example shows how to create a zoomable simple sinusoidal line chart.
+
+.. image:: zoomlinechart.png
+ :width: 400
+ :alt: Zoom Line Chart Screenshot
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import sys
+import math
+from PySide6.QtWidgets import QApplication, QMainWindow
+from PySide6.QtCore import Qt, QPointF, QRandomGenerator
+from PySide6.QtCharts import QChart, QLineSeries
+from PySide6.QtGui import QPainter
+
+import chartview
+import chart
+
+if __name__ == "__main__":
+
+ app = QApplication(sys.argv)
+
+ series = QLineSeries()
+
+ points = [
+ QPointF(float(i), math.sin(math.pi / 50 * i) * 100 + QRandomGenerator.global_().bounded(20))
+ for i in range(500)]
+
+ series.append(points)
+
+ line_chart = chart.Chart()
+ line_chart.addSeries(series)
+ line_chart.setTitle("Zoom in/out example")
+ line_chart.setAnimationOptions(QChart.SeriesAnimations)
+ line_chart.legend().hide()
+ line_chart.createDefaultAxes()
+
+ chart_view = chartview.ChartView(line_chart)
+ chart_view.setRenderHint(QPainter.Antialiasing, True)
+
+ window = QMainWindow()
+ window.setCentralWidget(chart_view)
+ window.resize(400, 300)
+ window.grabGesture(Qt.PanGesture)
+ window.grabGesture(Qt.PinchGesture)
+ window.show()
+
+ sys.exit(app.exec())
--- /dev/null
+{
+ "files": [
+ "chart.py",
+ "chartview.py",
+ "main.py"
+ ]
+}
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
from pathlib import Path
# load into shared memory
buffer = QBuffer()
- buffer.open(QIODeviceBase.WriteOnly)
+ buffer.open(QIODeviceBase.OpenModeFlag.WriteOnly)
out = QDataStream(buffer)
out << image
buffer.close()
mv = memoryview(self._shared_memory.constData())
buffer = QBuffer()
buffer.setData(mv.tobytes())
- buffer.open(QBuffer.ReadOnly)
+ buffer.open(QBuffer.OpenModeFlag.ReadOnly)
_in = QDataStream(buffer)
image = QImage()
_in >> image
-<ui version="4.0" >
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
<class>Dialog</class>
- <widget class="QDialog" name="Dialog" >
- <property name="geometry" >
+ <widget class="QDialog" name="Dialog">
+ <property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<height>322</height>
</rect>
</property>
- <property name="windowTitle" >
+ <property name="windowTitle">
<string>Dialog</string>
</property>
- <layout class="QGridLayout" >
- <item row="0" column="0" >
- <widget class="QPushButton" name="loadFromFileButton" >
- <property name="text" >
+ <layout class="QGridLayout">
+ <item row="0" column="0">
+ <widget class="QPushButton" name="loadFromFileButton">
+ <property name="text">
<string>Load Image From File...</string>
</property>
</widget>
</item>
- <item row="1" column="0" >
- <widget class="QLabel" name="label" >
- <property name="text" >
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
<string>Launch two of these dialogs. In the first, press the top button and load an image from a file. In the second, press the bottom button and display the loaded image from shared memory.</string>
</property>
- <property name="alignment" >
- <set>Qt::AlignCenter</set>
+ <property name="alignment">
+ <set>Qt::AlignmentFlag::AlignCenter</set>
</property>
- <property name="wordWrap" >
+ <property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
- <item row="2" column="0" >
- <widget class="QPushButton" name="loadFromSharedMemoryButton" >
- <property name="text" >
+ <item row="2" column="0">
+ <widget class="QPushButton" name="loadFromSharedMemoryButton">
+ <property name="text">
<string>Display Image From Shared Memory</string>
</property>
</widget>
--- /dev/null
+IPC: Shared Memory
+==================
+
+Demonstrates how to share image data between different processes using the
+Shared Memory IPC mechanism. It corresponds to the Qt example
+`Shared Memory Example <https://doc.qt.io/qt-6/qtcore-ipc-sharedmemory-example.html>`_.
+
+.. image:: sharedmemory.png
+ :align: center
+ :alt: sharedmemory screenshot
+ :width: 400
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the ipc/sharedmemory example from Qt v6.x"""
################################################################################
## Form generated from reading UI file 'dialog.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.label = QLabel(Dialog)
self.label.setObjectName(u"label")
- self.label.setAlignment(Qt.AlignCenter)
+ self.label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.label.setWordWrap(True)
self.gridLayout.addWidget(self.label, 1, 0, 1, 1)
.. image:: mimetypesbrowser.png
:width: 400
:alt: mimetypebrowser screenshot
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from mimetypemodel import MimeTypeModel
from PySide6.QtCore import (QDir, QFileInfo, QMimeDatabase, QModelIndex, Qt,
self.about_action = QAction(
"About Qt",
self,
- shortcut=QKeySequence(QKeySequence.HelpContents),
+ shortcut=QKeySequence(QKeySequence.StandardKey.HelpContents),
triggered=QApplication.aboutQt,
)
self.items = self.m_model.findItems(
"application/octet-stream",
- Qt.MatchContains | Qt.MatchFixedString | Qt.MatchRecursive,
+ Qt.MatchFlag.MatchContains | Qt.MatchFlag.MatchFixedString | Qt.MatchFlag.MatchRecursive
)
if self.items:
input_dialog = QInputDialog(self)
input_dialog.setWindowTitle("Find")
input_dialog.setLabelText("Text")
- if input_dialog.exec() != QDialog.Accepted:
+ if input_dialog.exec() != QDialog.DialogCode.Accepted:
return
value = input_dialog.textValue().strip()
self.m_find_matches.clear()
self.m_find_index = 0
- items = self.m_model.findItems(
- value, Qt.MatchContains | Qt.MatchFixedString | Qt.MatchRecursive
- )
+ flags = (Qt.MatchFlag.MatchContains | Qt.MatchFlag.MatchFixedString
+ | Qt.MatchFlag.MatchRecursive)
+ items = self.m_model.findItems(value, flags)
for item in items:
self.m_find_matches.append(self.m_model.indexFromItem(item))
self.m_find_previous_action.setEnabled(self.find_next_previous_enabled)
def _select_and_goto(self, index: QModelIndex):
- self.m_tree_view.scrollTo(index, QAbstractItemView.PositionAtCenter)
+ self.m_tree_view.scrollTo(index, QAbstractItemView.ScrollHint.PositionAtCenter)
self.m_tree_view.setCurrentIndex(index)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from typing import List
+from __future__ import annotations
from PySide6.QtCore import QMimeDatabase, QMimeType, QModelIndex, QObject, Qt, qWarning
from PySide6.QtGui import QStandardItem, QStandardItemModel
-mimeTypeRole = Qt.UserRole + 1
-iconQueriedRole = Qt.UserRole + 2
+mimeTypeRole = Qt.ItemDataRole.UserRole + 1
+iconQueriedRole = Qt.ItemDataRole.UserRole + 2
def createRow(t: QMimeType):
name_item = QStandardItem(t.name())
- flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
+ flags = Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled
name_item.setData(t, mimeTypeRole)
name_item.setData(False, iconQueriedRole)
name_item.setFlags(flags)
def populate(self):
mime_database = QMimeDatabase()
- all_types: List[QMimeType] = mime_database.allMimeTypes()
+ all_types: list[QMimeType] = mime_database.allMimeTypes()
# Move top level types to rear end of list, sort this partition,
# create top level items and truncate the list.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the corelib/mimetypes/mimetypebrowser example from from Qt"""
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/tools/settingseditor example from Qt v5.x"""
def __init__(self, parent=None):
self.bool_exp = QRegularExpression('^(true)|(false)$')
assert self.bool_exp.isValid()
- self.bool_exp.setPatternOptions(QRegularExpression.CaseInsensitiveOption)
+ self.bool_exp.setPatternOptions(QRegularExpression.PatternOption.CaseInsensitiveOption)
self.byteArray_exp = QRegularExpression(r'^[\x00-\xff]*$')
assert self.byteArray_exp.isValid()
min(int(match.captured(3)), 255),
min(int(match.captured(4)), 255))
if isinstance(original_value, QDate):
- value = QDate.fromString(text, Qt.ISODate)
+ value = QDate.fromString(text, Qt.DateFormat.ISODate)
return value if value.isValid() else None
if isinstance(original_value, QDateTime):
- value = QDateTime.fromString(text, Qt.ISODate)
+ value = QDateTime.fromString(text, Qt.DateFormat.ISODate)
return value if value.isValid() else None
if isinstance(original_value, QTime):
- value = QTime.fromString(text, Qt.ISODate)
+ value = QTime.fromString(text, Qt.DateFormat.ISODate)
return value if value.isValid() else None
if isinstance(original_value, QPoint):
match = self.point_exp.match(text)
self.load_ini_file(file_name)
def load_ini_file(self, file_name):
- settings = QSettings(file_name, QSettings.IniFormat)
- if settings.status() != QSettings.NoError:
+ settings = QSettings(file_name, QSettings.Format.IniFormat)
+ if settings.status() != QSettings.Status.NoError:
return
self.set_settings_object(settings)
self.fallbacks_action.setEnabled(False)
"Property List Files (*.plist)")
if file_name:
- settings = QSettings(file_name, QSettings.NativeFormat)
+ settings = QSettings(file_name, QSettings.Format.NativeFormat)
self.set_settings_object(settings)
self.fallbacks_action.setEnabled(False)
QLineEdit.Normal, 'HKEY_CURRENT_USER\\')
if ok and path != '':
- settings = QSettings(path, QSettings.NativeFormat)
+ settings = QSettings(path, QSettings.Format.NativeFormat)
self.set_settings_object(settings)
self.fallbacks_action.setEnabled(False)
self.locations_groupbox = QGroupBox("Setting Locations")
self.locations_table = QTableWidget()
- self.locations_table.setSelectionMode(QAbstractItemView.SingleSelection)
- self.locations_table.setSelectionBehavior(QAbstractItemView.SelectRows)
- self.locations_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
+ self.locations_table.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
+ self.locations_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
+ self.locations_table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
self.locations_table.setColumnCount(2)
self.locations_table.setHorizontalHeaderLabels(("Location", "Access"))
- self.locations_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
- self.locations_table.horizontalHeader().resizeSection(1, 180)
+ header = self.locations_table.horizontalHeader()
+ header.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
+ header.resizeSection(1, 180)
- self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ self.button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok
+ | QDialogButtonBox.StandardButton.Cancel)
self.format_combo.activated.connect(self.update_locations)
self.scope_cCombo.activated.connect(self.update_locations)
def format(self):
if self.format_combo.currentIndex() == 0:
- return QSettings.NativeFormat
+ return QSettings.Format.NativeFormat
else:
- return QSettings.IniFormat
+ return QSettings.Format.IniFormat
def scope(self):
if self.scope_cCombo.currentIndex() == 0:
- return QSettings.UserScope
+ return QSettings.Scope.UserScope
else:
- return QSettings.SystemScope
+ return QSettings.Scope.SystemScope
def organization(self):
return self.organization_combo.currentText()
for i in range(2):
if i == 0:
- if self.scope() == QSettings.SystemScope:
+ if self.scope() == QSettings.Scope.SystemScope:
continue
- actual_scope = QSettings.UserScope
+ actual_scope = QSettings.Scope.UserScope
else:
- actual_scope = QSettings.SystemScope
+ actual_scope = QSettings.Scope.SystemScope
for j in range(2):
if j == 0:
disable = False
else:
item1.setText("Read-only")
- self.button_box.button(QDialogButtonBox.Ok).setDisabled(disable)
+ self.button_box.button(QDialogButtonBox.StandardButton.Ok).setDisabled(disable)
else:
item1.setText("Read-only fallback")
if disable:
- item0.setFlags(item0.flags() & ~Qt.ItemIsEnabled)
- item1.setFlags(item1.flags() & ~Qt.ItemIsEnabled)
+ item0.setFlags(item0.flags() & ~Qt.ItemFlag.ItemIsEnabled)
+ item1.setFlags(item1.flags() & ~Qt.ItemFlag.ItemIsEnabled)
self.locations_table.setItem(row, 0, item0)
self.locations_table.setItem(row, 1, item1)
self.setItemDelegate(VariantDelegate(self._type_checker, self))
self.setHeaderLabels(("Setting", "Type", "Value"))
- self.header().setSectionResizeMode(0, QHeaderView.Stretch)
- self.header().setSectionResizeMode(2, QHeaderView.Stretch)
+ self.header().setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
+ self.header().setSectionResizeMode(2, QHeaderView.ResizeMode.Stretch)
self.settings = None
self.refresh_timer = QTimer()
self.group_icon = QIcon()
style = self.style()
- self.group_icon.addPixmap(style.standardPixmap(QStyle.SP_DirClosedIcon),
- QIcon.Normal, QIcon.Off)
- self.group_icon.addPixmap(style.standardPixmap(QStyle.SP_DirOpenIcon),
- QIcon.Normal, QIcon.On)
+ self.group_icon.addPixmap(style.standardPixmap(QStyle.StandardPixmap.SP_DirClosedIcon),
+ QIcon.Mode.Normal, QIcon.State.Off)
+ self.group_icon.addPixmap(style.standardPixmap(QStyle.StandardPixmap.SP_DirOpenIcon),
+ QIcon.Mode.Normal, QIcon.State.On)
self.key_icon = QIcon()
- self.key_icon.addPixmap(style.standardPixmap(QStyle.SP_FileIcon))
+ self.key_icon.addPixmap(style.standardPixmap(QStyle.StandardPixmap.SP_FileIcon))
self.refresh_timer.timeout.connect(self.maybe_refresh)
self.itemChanged.connect(self.update_setting)
def event(self, event):
- if event.type() == QEvent.WindowActivate:
+ if event.type() == QEvent.Type.WindowActivate:
if self.isActiveWindow() and self.auto_refresh:
self.maybe_refresh()
key = ancestor.text(0) + '/' + key
ancestor = ancestor.parent()
- self.settings.setValue(key, item.data(2, Qt.UserRole))
+ self.settings.setValue(key, item.data(2, Qt.ItemDataRole.UserRole))
if self.auto_refresh:
self.refresh()
child = self.child_at(parent, child_index)
child.setText(1, '')
child.setText(2, '')
- child.setData(2, Qt.UserRole, None)
+ child.setData(2, Qt.ItemDataRole.UserRole, None)
self.move_item_forward(parent, child_index, divider_index)
else:
child = self.create_item(group, parent, divider_index)
value = self.settings.value(key, type=value_type)
child.setText(1, value.__class__.__name__)
child.setText(2, VariantDelegate.display_text(value))
- child.setData(2, Qt.UserRole, value)
+ child.setData(2, Qt.ItemDataRole.UserRole, value)
while divider_index < self.child_count(parent):
self.delete_item(parent, divider_index)
item = QTreeWidgetItem(self, after)
item.setText(0, text)
- item.setFlags(item.flags() | Qt.ItemIsEditable)
+ item.setFlags(item.flags() | Qt.ItemFlag.ItemIsEditable)
return item
def delete_item(self, parent, index):
def paint(self, painter, option, index):
if index.column() == 2:
- value = index.model().data(index, Qt.UserRole)
+ value = index.model().data(index, Qt.ItemDataRole.UserRole)
if not self.is_supported_type(value):
my_option = QStyleOptionViewItem(option)
my_option.state &= ~QStyle.State_Enabled
if index.column() != 2:
return None
- original_value = index.model().data(index, Qt.UserRole)
+ original_value = index.model().data(index, Qt.ItemDataRole.UserRole)
if not self.is_supported_type(original_value):
return None
def setEditorData(self, editor, index):
if not editor:
return
- value = index.model().data(index, Qt.UserRole)
+ value = index.model().data(index, Qt.ItemDataRole.UserRole)
if isinstance(editor, QCheckBox):
editor.setCheckState(Qt.Checked if value else Qt.Unchecked)
elif isinstance(editor, QSpinBox):
state, text, _ = validator.validate(text, 0)
if state != QValidator.Acceptable:
return None
- original_value = index.model().data(index, Qt.UserRole)
+ original_value = index.model().data(index, Qt.ItemDataRole.UserRole)
return self._type_checker.from_string(text, original_value)
def setModelData(self, editor, model, index):
else:
value = self.value_from_lineedit(editor, model, index)
if value is not None:
- model.setData(index, value, Qt.UserRole)
- model.setData(index, self.display_text(value), Qt.DisplayRole)
+ model.setData(index, value, Qt.ItemDataRole.UserRole)
+ model.setData(index, self.display_text(value), Qt.ItemDataRole.DisplayRole)
@staticmethod
def is_supported_type(value):
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the corelib/threads/mandelbrot example from Qt v5.x, originating from PyQt"""
import sys
from PySide6.QtCore import (Signal, QMutex, QElapsedTimer, QMutexLocker,
- QPoint, QPointF, QSize, Qt, QThread,
+ QPoint, QPointF, QRectF, QSize, Qt, QThread,
QWaitCondition, Slot)
from PySide6.QtGui import QColor, QImage, QPainter, QPixmap, qRgb
from PySide6.QtWidgets import QApplication, QWidget
self._result_size = resultSize
if not self.isRunning():
- self.start(QThread.LowPriority)
+ self.start(QThread.Priority.LowPriority)
else:
self.restart = True
self.condition.wakeOne()
half_width = resultSize.width() // 2
half_height = resultSize.height() // 2
- image = QImage(resultSize, QImage.Format_RGB32)
+ image = QImage(resultSize, QImage.Format.Format_RGB32)
curpass = 0
g = pow(g * s, 0.8)
b = pow(b * s, 0.8)
- return qRgb(r * 255, g * 255, b * 255)
+ return qRgb(int(r * 255), int(g * 255), int(b * 255))
class MandelbrotWidget(QWidget):
self.thread.rendered_image.connect(self.update_pixmap)
self.setWindowTitle("Mandelbrot")
- self.setCursor(Qt.CrossCursor)
+ self.setCursor(Qt.CursorShape.CrossCursor)
self._info = ''
def paintEvent(self, event):
with QPainter(self) as painter:
- painter.fillRect(self.rect(), Qt.black)
+ painter.fillRect(self.rect(), Qt.GlobalColor.black)
if self.pixmap.isNull():
- painter.setPen(Qt.white)
- painter.drawText(self.rect(), Qt.AlignCenter,
+ painter.setPen(Qt.GlobalColor.white)
+ painter.drawText(self.rect(), Qt.AlignmentFlag.AlignCenter,
"Rendering initial image, please wait...")
return
metrics = painter.fontMetrics()
text_width = metrics.horizontalAdvance(text)
- painter.setPen(Qt.NoPen)
+ painter.setPen(Qt.PenStyle.NoPen)
painter.setBrush(QColor(0, 0, 0, 127))
- painter.drawRect((self.width() - text_width) / 2 - 5, 0, text_width + 10,
- metrics.lineSpacing() + 5)
- painter.setPen(Qt.white)
- painter.drawText((self.width() - text_width) / 2,
- metrics.leading() + metrics.ascent(), text)
+ box = QRectF((self.width() - text_width) / 2 - 5, 0,
+ text_width + 10, metrics.lineSpacing() + 5)
+ painter.drawRect(box)
+ painter.setPen(Qt.GlobalColor.white)
+ pos = QPointF((self.width() - text_width) / 2,
+ metrics.leading() + metrics.ascent())
+ painter.drawText(pos, text)
def resizeEvent(self, event):
self.thread.render(self._center_x, self._center_y, self._cur_scale, self.size())
def keyPressEvent(self, event):
- if event.key() == Qt.Key_Plus:
+ if event.key() == Qt.Key.Key_Plus:
self.zoom(ZOOM_IN_FACTOR)
- elif event.key() == Qt.Key_Minus:
+ elif event.key() == Qt.Key.Key_Minus:
self.zoom(ZOOM_OUT_FACTOR)
- elif event.key() == Qt.Key_Left:
+ elif event.key() == Qt.Key.Key_Left:
self.scroll(-SCROLL_STEP, 0)
- elif event.key() == Qt.Key_Right:
+ elif event.key() == Qt.Key.Key_Right:
self.scroll(+SCROLL_STEP, 0)
- elif event.key() == Qt.Key_Down:
+ elif event.key() == Qt.Key.Key_Down:
self.scroll(0, -SCROLL_STEP)
- elif event.key() == Qt.Key_Up:
+ elif event.key() == Qt.Key.Key_Up:
self.scroll(0, +SCROLL_STEP)
- elif event.key() == Qt.Key_Q:
+ elif event.key() == Qt.Key.Key_Q:
self.close()
else:
super(MandelbrotWidget, self).keyPressEvent(event)
self.zoom(pow(ZOOM_IN_FACTOR, num_steps))
def mousePressEvent(self, event):
- if event.buttons() == Qt.LeftButton:
+ if event.buttons() == Qt.MouseButton.LeftButton:
self._last_drag_pos = event.position()
def mouseMoveEvent(self, event):
- if event.buttons() & Qt.LeftButton:
+ if event.buttons() & Qt.MouseButton.LeftButton:
pos = event.position()
self._pixmap_offset += pos - self._last_drag_pos
self._last_drag_pos = pos
self.update()
def mouseReleaseEvent(self, event):
- if event.button() == Qt.LeftButton:
+ if event.button() == Qt.MouseButton.LeftButton:
pos = event.position()
self._pixmap_offset += pos - self._last_drag_pos
self._last_drag_pos = QPointF()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 QtDataVisualization example"""
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from enum import Enum
from math import sin, cos, degrees
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from graphmodifier import GraphModifier
reflectionCheckBox.setText("Show reflections")
reflectionCheckBox.setChecked(False)
- rotationSliderX = QSlider(Qt.Horizontal, self._barsWidget)
+ rotationSliderX = QSlider(Qt.Orientation.Horizontal, self._barsWidget)
rotationSliderX.setTickInterval(30)
rotationSliderX.setTickPosition(QSlider.TicksBelow)
rotationSliderX.setMinimum(-180)
rotationSliderX.setValue(0)
rotationSliderX.setMaximum(180)
- rotationSliderY = QSlider(Qt.Horizontal, self._barsWidget)
+ rotationSliderY = QSlider(Qt.Orientation.Horizontal, self._barsWidget)
rotationSliderY.setTickInterval(15)
rotationSliderY.setTickPosition(QSlider.TicksAbove)
rotationSliderY.setMinimum(-90)
rotationSliderY.setValue(0)
rotationSliderY.setMaximum(90)
- fontSizeSlider = QSlider(Qt.Horizontal, self._barsWidget)
+ fontSizeSlider = QSlider(Qt.Orientation.Horizontal, self._barsWidget)
fontSizeSlider.setTickInterval(10)
fontSizeSlider.setTickPosition(QSlider.TicksBelow)
fontSizeSlider.setMinimum(1)
axisTitlesFixedCB.setText("Axis titles fixed")
axisTitlesFixedCB.setChecked(True)
- axisLabelRotationSlider = QSlider(Qt.Horizontal, self._barsWidget)
+ axisLabelRotationSlider = QSlider(Qt.Orientation.Horizontal, self._barsWidget)
axisLabelRotationSlider.setTickInterval(10)
axisLabelRotationSlider.setTickPosition(QSlider.TicksBelow)
axisLabelRotationSlider.setMinimum(0)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from enum import Enum
from math import sin, cos, degrees
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from math import atan, degrees
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QPoint, Qt, Slot
from PySide6.QtGui import QLinearGradient, QVector3D
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Qt DataVisualization graphgallery example from Qt v6.x"""
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from math import cos, degrees, sqrt
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, QSize, Qt
from PySide6.QtWidgets import (QCheckBox, QComboBox, QCommandLinkButton,
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from surfacegraphmodifier import SurfaceGraphModifier
selectionVBox.addWidget(modeSliceColumnRB)
selectionGroupBox.setLayout(selectionVBox)
axisGroupBox = QGroupBox("Axis ranges")
- axisMinSliderX = QSlider(Qt.Horizontal)
+ axisMinSliderX = QSlider(Qt.Orientation.Horizontal)
axisMinSliderX.setMinimum(0)
axisMinSliderX.setTickInterval(1)
axisMinSliderX.setEnabled(True)
- axisMaxSliderX = QSlider(Qt.Horizontal)
+ axisMaxSliderX = QSlider(Qt.Orientation.Horizontal)
axisMaxSliderX.setMinimum(1)
axisMaxSliderX.setTickInterval(1)
axisMaxSliderX.setEnabled(True)
- axisMinSliderZ = QSlider(Qt.Horizontal)
+ axisMinSliderZ = QSlider(Qt.Orientation.Horizontal)
axisMinSliderZ.setMinimum(0)
axisMinSliderZ.setTickInterval(1)
axisMinSliderZ.setEnabled(True)
- axisMaxSliderZ = QSlider(Qt.Horizontal)
+ axisMaxSliderZ = QSlider(Qt.Orientation.Horizontal)
axisMaxSliderZ.setMinimum(1)
axisMaxSliderZ.setTickInterval(1)
axisMaxSliderZ.setEnabled(True)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
from math import sqrt, sin
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Qt
from PySide6.QtGui import QImage, QVector3D
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Signal
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Slot
from PySide6.QtDataVisualization import QBarDataProxy, QBarDataItem
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Signal
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
import sys
print(DESCRIPTION)
surface = Q3DSurface()
- surface.setFlags(surface.flags() ^ Qt.FramelessWindowHint)
+ surface.setFlags(surface.flags() ^ Qt.WindowType.FramelessWindowHint)
axis = surface.axisX()
axis.setTitle("X")
axis.setTitleVisible(True)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from math import sin, pi
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Qt DataVisualization qmlsurfacegallery example from Qt v6.x"""
qml_file = Path(__file__).resolve().parent / "qml" / "qmlsurfacegallery" / "main.qml"
viewer.setSource(QUrl.fromLocalFile(qml_file))
- viewer.setResizeMode(QQuickView.SizeRootObjectToView)
+ viewer.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
viewer.show()
ex = app.exec()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
selection_vbox.addWidget(mode_slice_column_rb)
selection_group_box.setLayout(selection_vbox)
- axis_min_slider_x = QSlider(Qt.Horizontal, self)
+ axis_min_slider_x = QSlider(Qt.Orientation.Horizontal, self)
axis_min_slider_x.setMinimum(0)
axis_min_slider_x.setTickInterval(1)
axis_min_slider_x.setEnabled(True)
- axis_max_slider_x = QSlider(Qt.Horizontal, self)
+ axis_max_slider_x = QSlider(Qt.Orientation.Horizontal, self)
axis_max_slider_x.setMinimum(1)
axis_max_slider_x.setTickInterval(1)
axis_max_slider_x.setEnabled(True)
- axis_min_slider_z = QSlider(Qt.Horizontal, self)
+ axis_min_slider_z = QSlider(Qt.Orientation.Horizontal, self)
axis_min_slider_z.setMinimum(0)
axis_min_slider_z.setTickInterval(1)
axis_min_slider_z.setEnabled(True)
- axis_max_slider_z = QSlider(Qt.Horizontal, self)
+ axis_max_slider_z = QSlider(Qt.Orientation.Horizontal, self)
axis_max_slider_z.setMinimum(1)
axis_max_slider_z.setTickInterval(1)
axis_max_slider_z.setEnabled(True)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import math
from pathlib import Path
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
selection_vbox.addWidget(mode_slice_column_rb)
selection_group_box.setLayout(selection_vbox)
- axis_min_slider_x = QSlider(Qt.Horizontal, self)
+ axis_min_slider_x = QSlider(Qt.Orientation.Horizontal, self)
axis_min_slider_x.setMinimum(0)
axis_min_slider_x.setTickInterval(1)
axis_min_slider_x.setEnabled(True)
- axis_max_slider_x = QSlider(Qt.Horizontal, self)
+ axis_max_slider_x = QSlider(Qt.Orientation.Horizontal, self)
axis_max_slider_x.setMinimum(1)
axis_max_slider_x.setTickInterval(1)
axis_max_slider_x.setEnabled(True)
- axis_min_slider_z = QSlider(Qt.Horizontal, self)
+ axis_min_slider_z = QSlider(Qt.Orientation.Horizontal, self)
axis_min_slider_z.setMinimum(0)
axis_min_slider_z.setTickInterval(1)
axis_min_slider_z.setEnabled(True)
- axis_max_slider_z = QSlider(Qt.Horizontal, self)
+ axis_max_slider_z = QSlider(Qt.Orientation.Horizontal, self)
axis_max_slider_z.setMinimum(1)
axis_max_slider_z.setTickInterval(1)
axis_max_slider_z.setEnabled(True)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import numpy as np
import math
SAMPLE_MAX = 8.0
-X_ROLE = Qt.UserRole + 1
-Y_ROLE = Qt.UserRole + 2
-Z_ROLE = Qt.UserRole + 3
+X_ROLE = Qt.ItemDataRole.UserRole + 1
+Y_ROLE = Qt.ItemDataRole.UserRole + 2
+Z_ROLE = Qt.ItemDataRole.UserRole + 3
class SqrtSinModel(QAbstractTableModel):
def columnCount(self, index=QModelIndex()):
return self._x.size
- def data(self, index, role=Qt.DisplayRole):
+ def data(self, index, role=Qt.ItemDataRole.DisplayRole):
row = index.row()
col = index.column()
if role == X_ROLE:
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
selection_vbox.addWidget(mode_slice_column_rb)
selection_group_box.setLayout(selection_vbox)
- axis_min_slider_x = QSlider(Qt.Horizontal, self)
+ axis_min_slider_x = QSlider(Qt.Orientation.Horizontal, self)
axis_min_slider_x.setMinimum(0)
axis_min_slider_x.setTickInterval(1)
axis_min_slider_x.setEnabled(True)
- axis_max_slider_x = QSlider(Qt.Horizontal, self)
+ axis_max_slider_x = QSlider(Qt.Orientation.Horizontal, self)
axis_max_slider_x.setMinimum(1)
axis_max_slider_x.setTickInterval(1)
axis_max_slider_x.setEnabled(True)
- axis_min_slider_z = QSlider(Qt.Horizontal, self)
+ axis_min_slider_z = QSlider(Qt.Orientation.Horizontal, self)
axis_min_slider_z.setMinimum(0)
axis_min_slider_z.setTickInterval(1)
axis_min_slider_z.setEnabled(True)
- axis_max_slider_z = QSlider(Qt.Horizontal, self)
+ axis_max_slider_z = QSlider(Qt.Orientation.Horizontal, self)
axis_max_slider_z.setMinimum(1)
axis_max_slider_z.setTickInterval(1)
axis_max_slider_z.setEnabled(True)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import numpy as np
import math
--- /dev/null
+D-Bus List Names Example
+========================
+
+Shows how to access the D-Bus bus daemon service.
+
+List Names is a command-line example which shows how to access the Qt D-Bus bus
+daemon service. The example prints various information about the bus daemon service
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the QtDBus listnames example from Qt v6.x"""
--- /dev/null
+D-Bus Ping Pong Example
+=======================
+
+Demonstrates a simple message system using D-Bus.
+
+Ping Pong is a command-line example that demonstrates the basics of Qt D-Bus.
+A message is sent to another application and there is a confirmation of the
+message.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the QtDBus pingpong example from Qt v6.x (ping client)"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the QtDBus pingpong example from Qt v6.x (pong server)"""
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject
from PySide6.QtQml import QmlAnonymous
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from functools import partial
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Qt RESTful API client demo from Qt v6.x"""
if not engine.rootObjects():
sys.exit(-1)
- ex = app.exec()
+ exit_code = app.exec()
del engine
- sys.exit(ex)
+ sys.exit(exit_code)
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from dataclasses import dataclass
@QmlElement
class ColorUserModel (QAbstractListModel):
- IdRole = Qt.UserRole + 1
- EmailRole = Qt.UserRole + 2
- AvatarRole = Qt.UserRole + 3
+ IdRole = Qt.ItemDataRole.UserRole + 1
+ EmailRole = Qt.ItemDataRole.UserRole + 2
+ AvatarRole = Qt.ItemDataRole.UserRole + 3
def __init__(self, parent=None):
super().__init__(parent)
@QmlElement
class ColorModel (QAbstractListModel):
- IdRole = Qt.UserRole + 1
- ColorRole = Qt.UserRole + 2
- NameRole = Qt.UserRole + 3
- PantoneValueRole = Qt.UserRole + 4
+ IdRole = Qt.ItemDataRole.UserRole + 1
+ ColorRole = Qt.ItemDataRole.UserRole + 2
+ NameRole = Qt.ItemDataRole.UserRole + 3
+ PantoneValueRole = Qt.ItemDataRole.UserRole + 4
def __init__(self, parent=None):
super().__init__(parent)
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Property, Signal, ClassInfo
from PySide6.QtNetwork import (QNetworkAccessManager, QRestAccessManager,
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject
if not self.hasContent():
self.statusMessage("No content to print.", type)
return
- printer = QPrinter(QPrinter.HighResolution)
+ printer = QPrinter(QPrinter.PrinterMode.HighResolution)
dlg = QPrintDialog(printer, self.mainWindow())
dlg.setWindowTitle("Print Document")
- if dlg.exec() == QDialog.Accepted:
+ if dlg.exec() == QDialog.DialogCode.Accepted:
self.printDocument(printer)
else:
self.statusMessage("Printing canceled!", type)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import json
return None
item = self.itemFromIndex(index)
- if role == Qt.DisplayRole:
+ if role == Qt.ItemDataRole.DisplayRole:
if index.column() == 0:
return item.key()
if index.column() == 1:
return item.value()
- elif role == Qt.EditRole:
+ elif role == Qt.ItemDataRole.EditRole:
if index.column() == 1:
return item.value()
return None
def headerData(self, section, orientation, role):
return (self._headers[section]
- if role == Qt.DisplayRole and orientation == Qt.Horizontal else None)
+ if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal
+ else None)
def index(self, row, column, parent=QModelIndex()):
if not self.hasIndex(row, column, parent):
zoomInIcon = QIcon.fromTheme(QIcon.ThemeIcon.ZoomIn)
a = menu.addAction(zoomInIcon, "&+Expand all", self._tree.expandAll)
tb.addAction(a)
- a.setPriority(QAction.LowPriority)
- a.setShortcut(QKeySequence.New)
+ a.setPriority(QAction.Priority.LowPriority)
+ a.setShortcut(QKeySequence.StandardKey.New)
zoomOutIcon = QIcon.fromTheme(QIcon.ThemeIcon.ZoomOut)
a = menu.addAction(zoomOutIcon, "&-Collapse all", self._tree.collapseAll)
tb.addAction(a)
- a.setPriority(QAction.LowPriority)
- a.setShortcut(QKeySequence.New)
+ a.setPriority(QAction.Priority.LowPriority)
+ a.setShortcut(QKeySequence.StandardKey.New)
if not self._searchKey:
self._searchKey = QLineEdit(tb)
index = self._tree.model().index(i, 0)
self._toplevel.addItem(index.data())
item = self._toplevel.item(i)
- item.setData(Qt.UserRole, index)
+ item.setData(Qt.ItemDataRole.UserRole, index)
item.setToolTip(f"Toplevel Item {i}")
self._toplevel.setAcceptDrops(True)
self._tree.setDragEnabled(True)
- self._tree.setContextMenuPolicy(Qt.CustomContextMenu)
- self._toplevel.setContextMenuPolicy(Qt.CustomContextMenu)
+ self._tree.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
+ self._toplevel.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self._toplevel.itemClicked.connect(self.onTopLevelItemClicked)
self._toplevel.itemDoubleClicked.connect(self.onTopLevelItemDoubleClicked)
self.disablePrinting()
file_name = QDir.toNativeSeparators(self._file.fileName())
type = "open"
- self._file.open(QIODevice.ReadOnly)
+ self._file.open(QIODevice.OpenModeFlag.ReadOnly)
self._text = self._file.readAll().data().decode("utf-8")
self._file.close()
return self._tree.model() is not None
def indexOf(self, item):
- return QModelIndex(item.data(Qt.UserRole))
+ return QModelIndex(item.data(Qt.ItemDataRole.UserRole))
@Slot(QListWidgetItem)
def onTopLevelItemClicked(self, item):
if not index.isValid():
return
- item = QListWidgetItem(index.data(Qt.DisplayRole), self._toplevel)
- item.setData(Qt.UserRole, index)
+ item = QListWidgetItem(index.data(Qt.ItemDataRole.DisplayRole), self._toplevel)
+ item.setData(Qt.ItemDataRole.UserRole, index)
# Set a tooltip that shows where the item is located in the tree
parent = index.parent()
- tooltip = index.data(Qt.DisplayRole).toString()
+ tooltip = index.data(Qt.ItemDataRole.DisplayRole).toString()
while parent.isValid():
- tooltip = parent.data(Qt.DisplayRole).toString() + "." + tooltip
+ tooltip = parent.data(Qt.ItemDataRole.DisplayRole).toString() + "." + tooltip
parent = parent.parent()
item.setToolTip(tooltip)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Qt Document Viewer demo from Qt v6.x"""
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWidgets import (QDialog, QFileDialog, QMainWindow, QMessageBox)
from PySide6.QtCore import (QDir, QFile, QFileInfo, QSettings, Slot)
def onActionOpenTriggered(self):
fileDialog = QFileDialog(self, "Open Document",
self._currentDir.absolutePath())
- while (fileDialog.exec() == QDialog.Accepted
+ while (fileDialog.exec() == QDialog.DialogCode.Accepted
and not self.openFile(fileDialog.selectedFiles()[0])):
pass
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<widget class="QTabWidget" name="tabWidget">
<property name="tabPosition">
- <enum>QTabWidget::West</enum>
+ <enum>QTabWidget::TabPosition::West</enum>
</property>
<property name="currentIndex">
<number>0</number>
<x>0</x>
<y>0</y>
<width>798</width>
- <height>479</height>
+ <height>472</height>
</rect>
</property>
</widget>
<x>0</x>
<y>0</y>
<width>983</width>
- <height>23</height>
+ <height>26</height>
</rect>
</property>
<widget class="QMenu" name="qtFileMenu">
</action>
<action name="actionQuit">
<property name="icon">
- <iconset theme="application-exit">
- <normaloff>.</normaloff>.</iconset>
+ <iconset theme="application-exit"/>
</property>
<property name="text">
<string>Quit</string>
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from math import sqrt
from PySide6.QtWidgets import (QListView, QTreeView)
-from PySide6.QtGui import QIcon, QPainter
+from PySide6.QtGui import QIcon, QKeySequence, QPainter
from PySide6.QtCore import (QDir, QIODevice, QModelIndex,
QPointF, Slot)
from PySide6.QtPrintSupport import QPrinter
self._toolBar.addSeparator()
self._toolBar.addWidget(self._zoomSelector)
- actionZoomIn = self._toolBar.addAction("Zoom in")
- actionZoomIn.setToolTip("Increase zoom level")
icon = QIcon.fromTheme(QIcon.ThemeIcon.ZoomIn,
QIcon(":/demos/documentviewer/images/zoom-in.png"))
- actionZoomIn.setIcon(icon)
- self._toolBar.addAction(actionZoomIn)
+ actionZoomIn = self._toolBar.addAction(icon, "Zoom in", QKeySequence.StandardKey.ZoomIn)
+ actionZoomIn.setToolTip("Increase zoom level")
actionZoomIn.triggered.connect(self.onActionZoomInTriggered)
- actionZoomOut = self._toolBar.addAction("Zoom out")
- actionZoomOut.setToolTip("Decrease zoom level")
icon = QIcon.fromTheme(QIcon.ThemeIcon.ZoomIn,
QIcon(":/demos/documentviewer/images/zoom-out.png"))
- actionZoomOut.setIcon(icon)
- self._toolBar.addAction(actionZoomOut)
+ actionZoomOut = self._toolBar.addAction(icon, "Zoom out", QKeySequence.StandardKey.ZoomOut)
+ actionZoomOut.setToolTip("Decrease zoom level")
actionZoomOut.triggered.connect(self.onActionZoomOutTriggered)
nav.backAvailableChanged.connect(self._actionBack.setEnabled)
def openPdfFile(self):
self.disablePrinting()
- if self._file.open(QIODevice.ReadOnly):
+ if self._file.open(QIODevice.OpenModeFlag.ReadOnly):
self._document.load(self._file)
documentTitle = self._document.metaData(QPdfDocument.MetaDataField.Title)
# Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWidgets import QComboBox
from PySide6.QtCore import Signal, Slot
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWidgets import QMenu
from PySide6.QtCore import Signal, Slot
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from enum import Enum, auto
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWidgets import (QDialog, QFileDialog,
QPlainTextEdit)
cutIcon = QIcon.fromTheme(QIcon.ThemeIcon.EditCut,
QIcon(":/demos/documentviewer/images/cut.png"))
cutAct = QAction(cutIcon, "Cut", self)
- cutAct.setShortcuts(QKeySequence.Cut)
+ cutAct.setShortcuts(QKeySequence.StandardKey.Cut)
cutAct.setStatusTip("Cut the current selection's contents to the clipboard")
cutAct.triggered.connect(self._textEdit.cut)
editMenu.addAction(cutAct)
copyIcon = QIcon.fromTheme(QIcon.ThemeIcon.EditCopy,
QIcon(":/demos/documentviewer/images/copy.png"))
copyAct = QAction(copyIcon, "Copy", self)
- copyAct.setShortcuts(QKeySequence.Copy)
+ copyAct.setShortcuts(QKeySequence.StandardKey.Copy)
copyAct.setStatusTip("Copy the current selection's contents to the clipboard")
copyAct.triggered.connect(self._textEdit.copy)
editMenu.addAction(copyAct)
pasteIcon = QIcon.fromTheme(QIcon.ThemeIcon.EditPaste,
QIcon(":/demos/documentviewer/images/paste.png"))
pasteAct = QAction(pasteIcon, "Paste", self)
- pasteAct.setShortcuts(QKeySequence.Paste)
+ pasteAct.setShortcuts(QKeySequence.StandardKey.Paste)
pasteAct.setStatusTip("Paste the clipboard's contents into the current selection")
pasteAct.triggered.connect(self._textEdit.paste)
editMenu.addAction(pasteAct)
def openFile(self):
type = "open"
file_name = QDir.toNativeSeparators(self._file.fileName())
- if not self._file.open(QFile.ReadOnly | QFile.Text):
+ if not self._file.open(QFile.OpenModeFlag.ReadOnly
+ | QFile.OpenModeFlag.Text):
err = self._file.errorString()
self.statusMessage(f"Cannot read file {file_name}:\n{err}.", type)
return
in_str = QTextStream(self._file)
- QGuiApplication.setOverrideCursor(Qt.WaitCursor)
+ QGuiApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
if self._textEdit.toPlainText():
self._textEdit.clear()
self.disablePrinting()
def saveFile(self, file):
file_name = QDir.toNativeSeparators(self._file.fileName())
errorMessage = ""
- QGuiApplication.setOverrideCursor(Qt.WaitCursor)
- if file.open(QFile.WriteOnly | QFile.Text):
+ QGuiApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
+ if file.open(QFile.OpenModeFlag.WriteOnly | QFile.OpenModeFlag.Text):
out = QTextStream(file)
out << self._textEdit.toPlainText()
else:
def saveDocumentAs(self):
dialog = QFileDialog(self.mainWindow())
dialog.setWindowModality(Qt.WindowModal)
- dialog.setAcceptMode(QFileDialog.AcceptSave)
- if dialog.exec() != QDialog.Accepted:
+ dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
+ if dialog.exec() != QDialog.DialogCode.Accepted:
return False
files = dialog.selectedFiles()
################################################################################
## Form generated from reading UI file 'mainwindow.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
MainWindow.setObjectName(u"MainWindow")
MainWindow.resize(983, 602)
icon = QIcon()
- icon.addFile(u":/demos/documentviewer/images/qt-logo.png", QSize(), QIcon.Normal, QIcon.Off)
+ icon.addFile(u":/demos/documentviewer/images/qt-logo.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
MainWindow.setWindowIcon(icon)
self.actionOpen = QAction(MainWindow)
self.actionOpen.setObjectName(u"actionOpen")
icon1 = QIcon()
- icon1.addFile(u":/demos/documentviewer/images/open.png", QSize(), QIcon.Normal, QIcon.Off)
+ icon1.addFile(u":/demos/documentviewer/images/open.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionOpen.setIcon(icon1)
self.actionAbout = QAction(MainWindow)
self.actionAbout.setObjectName(u"actionAbout")
if QIcon.hasThemeIcon(iconThemeName):
icon2 = QIcon.fromTheme(iconThemeName)
else:
- icon2.addFile(u":/demos/documentviewer/images/help-about.svgz", QSize(), QIcon.Normal, QIcon.Off)
+ icon2.addFile(u":/demos/documentviewer/images/help-about.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionAbout.setIcon(icon2)
self.actionForward = QAction(MainWindow)
self.actionForward.setObjectName(u"actionForward")
icon3 = QIcon()
- icon3.addFile(u":/demos/documentviewer/images/go-next.svgz", QSize(), QIcon.Normal, QIcon.Off)
+ icon3.addFile(u":/demos/documentviewer/images/go-next.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionForward.setIcon(icon3)
self.actionBack = QAction(MainWindow)
self.actionBack.setObjectName(u"actionBack")
icon4 = QIcon()
- icon4.addFile(u":/demos/documentviewer/images/go-previous.svgz", QSize(), QIcon.Normal, QIcon.Off)
+ icon4.addFile(u":/demos/documentviewer/images/go-previous.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionBack.setIcon(icon4)
self.actionPrint = QAction(MainWindow)
self.actionPrint.setObjectName(u"actionPrint")
if QIcon.hasThemeIcon(iconThemeName):
icon5 = QIcon.fromTheme(iconThemeName)
else:
- icon5.addFile(u":/demos/documentviewer/images/print2x.png", QSize(), QIcon.Normal, QIcon.Off)
+ icon5.addFile(u":/demos/documentviewer/images/print2x.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionPrint.setIcon(icon5)
self.actionAboutQt = QAction(MainWindow)
self.actionAboutQt.setObjectName(u"actionAboutQt")
icon6 = QIcon()
- icon6.addFile(u":/demos/documentviewer/images/qt-logo.png", QSize(), QIcon.Normal, QIcon.Off)
- icon6.addFile(u":/demos/documentviewer/images/qt-logo.png", QSize(), QIcon.Normal, QIcon.On)
+ icon6.addFile(u":/demos/documentviewer/images/qt-logo.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
+ icon6.addFile(u":/demos/documentviewer/images/qt-logo.png", QSize(), QIcon.Mode.Normal, QIcon.State.On)
self.actionAboutQt.setIcon(icon6)
self.actionRecent = QAction(MainWindow)
self.actionRecent.setObjectName(u"actionRecent")
icon7 = QIcon()
- icon7.addFile(u":/demos/documentviewer/images/document-open-recent.svgz", QSize(), QIcon.Normal, QIcon.Off)
+ icon7.addFile(u":/demos/documentviewer/images/document-open-recent.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionRecent.setIcon(icon7)
self.actionQuit = QAction(MainWindow)
self.actionQuit.setObjectName(u"actionQuit")
- icon8 = QIcon()
- iconThemeName = u"application-exit"
- if QIcon.hasThemeIcon(iconThemeName):
- icon8 = QIcon.fromTheme(iconThemeName)
- else:
- icon8.addFile(u".", QSize(), QIcon.Normal, QIcon.Off)
-
+ icon8 = QIcon(QIcon.fromTheme(u"application-exit"))
self.actionQuit.setIcon(icon8)
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget")
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.splitter = QSplitter(self.viewArea)
self.splitter.setObjectName(u"splitter")
- self.splitter.setOrientation(Qt.Horizontal)
+ self.splitter.setOrientation(Qt.Orientation.Horizontal)
self.tabWidget = QTabWidget(self.splitter)
self.tabWidget.setObjectName(u"tabWidget")
- self.tabWidget.setTabPosition(QTabWidget.West)
+ self.tabWidget.setTabPosition(QTabWidget.TabPosition.West)
self.bookmarkTab = QWidget()
self.bookmarkTab.setObjectName(u"bookmarkTab")
self.tabWidget.addTab(self.bookmarkTab, "")
self.scrollArea.setWidgetResizable(True)
self.scrollAreaWidgetContents = QWidget()
self.scrollAreaWidgetContents.setObjectName(u"scrollAreaWidgetContents")
- self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 798, 479))
+ self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 798, 472))
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.splitter.addWidget(self.scrollArea)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(MainWindow)
self.menubar.setObjectName(u"menubar")
- self.menubar.setGeometry(QRect(0, 0, 983, 23))
+ self.menubar.setGeometry(QRect(0, 0, 983, 26))
self.qtFileMenu = QMenu(self.menubar)
self.qtFileMenu.setObjectName(u"qtFileMenu")
self.menuHelp = QMenu(self.menubar)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from enum import Enum, auto
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Window
+import QtQuick3D
+import QtQuick3D.Helpers
+
+import OSMBuildings
+
+Window {
+ width: 1024
+ height: 768
+ visible: true
+ title: qsTr("OSM Buildings")
+
+ OSMManager {
+ id: osmManager
+
+ onMapsDataReady: function( mapData, tileX, tileY, zoomLevel ){
+ mapModels.addModel(mapData, tileX, tileY, zoomLevel)
+ }
+ }
+
+ Component {
+ id: chunkModelMap
+ Node {
+ property variant mapData: null
+ property int tileX: 0
+ property int tileY: 0
+ property int zoomLevel: 0
+ Model {
+ id: basePlane
+ position: Qt.vector3d( osmManager.tileSizeX * tileX, osmManager.tileSizeY * -tileY, 0.0 )
+ scale: Qt.vector3d( osmManager.tileSizeX / 100., osmManager.tileSizeY / 100., 0.5)
+ source: "#Rectangle"
+ materials: [
+ CustomMaterial {
+ property TextureInput tileTexture: TextureInput {
+ enabled: true
+ texture: Texture {
+ textureData: CustomTextureData {
+ Component.onCompleted: setImageData( mapData )
+ } }
+ }
+ shadingMode: CustomMaterial.Shaded
+ cullMode: Material.BackFaceCulling
+ fragmentShader: "customshadertiles.frag"
+ }
+ ]
+ }
+ }
+ }
+
+
+ View3D {
+ id: v3d
+ anchors.fill: parent
+
+ environment: ExtendedSceneEnvironment {
+ id: env
+ backgroundMode: SceneEnvironment.Color
+ clearColor: "#8099b3"
+ fxaaEnabled: true
+ fog: Fog {
+ id: theFog
+ color:"#8099b3"
+ enabled: true
+ depthEnabled: true
+ depthFar: 600
+ }
+ }
+
+ Node {
+ id: originNode
+ eulerRotation: Qt.vector3d(50.0, 0.0, 0.0)
+ PerspectiveCamera {
+ id: cameraNode
+ frustumCullingEnabled: true
+ clipFar: 600
+ clipNear: 100
+ fieldOfView: 90
+ z: 100
+
+ onZChanged: originNode.updateManagerCamera()
+
+ }
+ Component.onCompleted: updateManagerCamera()
+
+ onPositionChanged: updateManagerCamera()
+
+ onRotationChanged: updateManagerCamera()
+
+ function updateManagerCamera(){
+ osmManager.setCameraProperties( originNode.position,
+ originNode.right, cameraNode.z,
+ cameraController.minimumZoom,
+ cameraController.maximumZoom,
+ originNode.eulerRotation.x,
+ cameraController.minimumTilt,
+ cameraController.maximumTilt )
+ }
+ }
+
+ DirectionalLight {
+ color: Qt.rgba(1.0, 1.0, 0.95, 1.0)
+ ambientColor: Qt.rgba(0.5, 0.45, 0.45, 1.0)
+ rotation: Quaternion.fromEulerAngles(-10, -45, 0)
+ }
+
+ Node {
+ id: mapModels
+
+ function addModel(mapData, tileX, tileY, zoomLevel)
+ {
+ chunkModelMap.createObject( mapModels, { "mapData": mapData,
+ "tileX": tileX,
+ "tileY": tileY,
+ "zoomLevel": zoomLevel
+ } )
+ }
+ }
+
+ OSMCameraController {
+ id: cameraController
+ origin: originNode
+ camera: cameraNode
+ }
+ }
+
+ Item {
+ id: tokenArea
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ anchors.margins: 10
+ Text {
+ id: tokenInputArea
+ visible: false
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ color: "white"
+ styleColor: "black"
+ style: Text.Outline
+ text: "Open street map tile token: "
+ Rectangle {
+ border.width: 1
+ border.color: "black"
+ anchors.fill: tokenTxtInput
+ anchors.rightMargin: -30
+ Text {
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.topMargin: 2
+ anchors.rightMargin: 8
+ color: "blue"
+ styleColor: "white"
+ style: Text.Outline
+ text: "OK"
+ Behavior on scale {
+ NumberAnimation {
+ easing.type: Easing.OutBack
+ }
+ }
+ MouseArea {
+ anchors.fill: parent
+ anchors.margins: -10
+ onPressedChanged: {
+ if (pressed)
+ parent.scale = 0.9
+ else
+ parent.scale = 1.0
+ }
+ onClicked: {
+ tokenInputArea.visible = false
+ osmManager.setToken(tokenTxtInput.text)
+ tokenWarning.demoToken = osmManager.isDemoToken()
+ tokenWarning.visible = true
+ }
+ }
+ }
+ }
+ TextInput {
+ id: tokenTxtInput
+ clip: true
+ anchors.left: parent.right
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: -3
+ height: tokenTxtInput.contentHeight + 5
+ width: 110
+ leftPadding: 5
+ rightPadding: 5
+ }
+ }
+
+ Text {
+ id: tokenWarning
+ property bool demoToken: true
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ color: "white"
+ styleColor: "black"
+ style: Text.Outline
+ text: demoToken ? "You are using the OSM limited demo token " :
+ "You are using a token "
+ Text {
+ anchors.left: parent.right
+ color: "blue"
+ styleColor: "white"
+ style: Text.Outline
+ text: "click here to change"
+ Behavior on scale {
+ NumberAnimation {
+ easing.type: Easing.OutBack
+ }
+ }
+ MouseArea {
+ anchors.fill: parent
+ onPressedChanged: {
+ if (pressed)
+ parent.scale = 0.9
+ else
+ parent.scale = 1.0
+ }
+ onClicked: {
+ tokenWarning.visible = false
+ tokenTxtInput.text = osmManager.token()
+ tokenInputArea.visible = true
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick3D
+
+Item {
+ id: root
+ required property Node origin
+ required property Camera camera
+
+ property real xSpeed: 0.05
+ property real ySpeed: 0.05
+
+ property bool xInvert: false
+ property bool yInvert: false
+
+ property bool mouseEnabled: true
+ property bool panEnabled: true
+
+ readonly property bool inputsNeedProcessing: status.useMouse || status.isPanning
+
+ readonly property real minimumZoom: 30
+ readonly property real maximumZoom: 200
+
+ readonly property real minimumTilt: 0
+ readonly property real maximumTilt: 80
+
+ implicitWidth: parent.width
+ implicitHeight: parent.height
+
+ Connections {
+ target: camera
+ Component.onCompleted: {
+ onZChanged()
+ }
+
+ function onZChanged() {
+ // Adjust near/far values based on distance
+ let distance = camera.z
+ if (distance < 1) {
+ camera.clipNear = 0.01
+ camera.clipFar = 100
+ } else if (distance < 100) {
+ camera.clipNear = 0.1
+ camera.clipFar = 1000
+ } else {
+ camera.clipNear = 1
+ camera.clipFar = 10000
+ }
+ }
+ }
+
+ DragHandler {
+ id: dragHandler
+ target: null
+ enabled: mouseEnabled
+ acceptedModifiers: Qt.NoModifier
+ acceptedButtons: Qt.RightButton
+ onCentroidChanged: {
+ mouseMoved(Qt.vector2d(centroid.position.x, centroid.position.y), false);
+ }
+
+ onActiveChanged: {
+ if (active)
+ mousePressed(Qt.vector2d(centroid.position.x, centroid.position.y));
+ else
+ mouseReleased(Qt.vector2d(centroid.position.x, centroid.position.y));
+ }
+ }
+
+ DragHandler {
+ id: ctrlDragHandler
+ target: null
+ enabled: mouseEnabled && panEnabled
+ //acceptedModifiers: Qt.ControlModifier
+ onCentroidChanged: {
+ panEvent(Qt.vector2d(centroid.position.x, centroid.position.y));
+ }
+
+ onActiveChanged: {
+ if (active)
+ startPan(Qt.vector2d(centroid.position.x, centroid.position.y));
+ else
+ endPan();
+ }
+ }
+
+ PinchHandler {
+ id: pinchHandler
+ target: null
+ enabled: mouseEnabled
+
+ property real distance: 0.0
+ onCentroidChanged: {
+ panEvent(Qt.vector2d(centroid.position.x, centroid.position.y))
+ }
+
+ onActiveChanged: {
+ if (active) {
+ startPan(Qt.vector2d(centroid.position.x, centroid.position.y))
+ distance = root.camera.z
+ } else {
+ endPan()
+ distance = 0.0
+ }
+ }
+ onScaleChanged: {
+
+ camera.z = distance * (1 / scale)
+ camera.z = Math.min(Math.max(camera.z, minimumZoom), maximumZoom)
+ }
+ }
+
+ TapHandler {
+ onTapped: root.forceActiveFocus()
+ }
+
+ WheelHandler {
+ id: wheelHandler
+ orientation: Qt.Vertical
+ target: null
+ enabled: mouseEnabled
+ onWheel: event => {
+ let delta = -event.angleDelta.y * 0.01;
+ camera.z += camera.z * 0.1 * delta
+ camera.z = Math.min(Math.max(camera.z, minimumZoom), maximumZoom)
+ }
+ }
+
+ function mousePressed(newPos) {
+ root.forceActiveFocus()
+ status.currentPos = newPos
+ status.lastPos = newPos
+ status.useMouse = true;
+ }
+
+ function mouseReleased(newPos) {
+ status.useMouse = false;
+ }
+
+ function mouseMoved(newPos: vector2d) {
+ status.currentPos = newPos;
+ }
+
+ function startPan(pos: vector2d) {
+ status.isPanning = true;
+ status.currentPanPos = pos;
+ status.lastPanPos = pos;
+ }
+
+ function endPan() {
+ status.isPanning = false;
+ }
+
+ function panEvent(newPos: vector2d) {
+ status.currentPanPos = newPos;
+ }
+
+ FrameAnimation {
+ id: updateTimer
+ running: root.inputsNeedProcessing
+ onTriggered: status.processInput(frameTime * 100)
+ }
+
+ QtObject {
+ id: status
+
+ property bool useMouse: false
+ property bool isPanning: false
+
+ property vector2d lastPos: Qt.vector2d(0, 0)
+ property vector2d lastPanPos: Qt.vector2d(0, 0)
+ property vector2d currentPos: Qt.vector2d(0, 0)
+ property vector2d currentPanPos: Qt.vector2d(0, 0)
+
+ property real rotateAlongZ: 0
+ property real rotateAlongXY: 50.0
+
+ function processInput(frameDelta) {
+ if (useMouse) {
+ // Get the delta
+ var delta = Qt.vector2d(lastPos.x - currentPos.x,
+ lastPos.y - currentPos.y);
+
+ var rotateX = delta.x * xSpeed * frameDelta
+ if ( xInvert )
+ rotateX = -rotateX
+ rotateAlongZ += rotateX;
+ let rotateAlongZRad = rotateAlongZ * (Math.PI / 180.)
+
+ origin.rotate(rotateX, Qt.vector3d(0.0, 0.0, -1.0), Node.SceneSpace)
+
+ var rotateY = delta.y * -ySpeed * frameDelta
+ if ( yInvert )
+ rotateY = -rotateY;
+
+ let preRotateAlongXY = rotateAlongXY + rotateY
+ if ( preRotateAlongXY <= maximumTilt && preRotateAlongXY >= minimumTilt )
+ {
+ rotateAlongXY = preRotateAlongXY
+ origin.rotate(rotateY, Qt.vector3d(Math.cos(rotateAlongZRad), Math.sin(-rotateAlongZRad), 0.0), Node.SceneSpace)
+ }
+
+ lastPos = currentPos;
+ }
+
+ if (isPanning) {
+ let delta = currentPanPos.minus(lastPanPos);
+ delta.x = -delta.x
+
+ delta.x = (delta.x / root.width) * camera.z * frameDelta
+ delta.y = (delta.y / root.height) * camera.z * frameDelta
+
+ let velocity = Qt.vector3d(0, 0, 0)
+ // X Movement
+ let xDirection = origin.right
+ velocity = velocity.plus(Qt.vector3d(xDirection.x * delta.x,
+ xDirection.y * delta.x,
+ xDirection.z * delta.x));
+ // Z Movement
+ let zDirection = origin.right.crossProduct(Qt.vector3d(0.0, 0.0, -1.0))
+ velocity = velocity.plus(Qt.vector3d(zDirection.x * delta.y,
+ zDirection.y * delta.y,
+ zDirection.z * delta.y));
+
+ origin.position = origin.position.plus(velocity)
+
+ lastPanPos = currentPanPos
+ }
+ }
+ }
+
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+void MAIN() {
+ vec2 tc = UV0;
+ BASE_COLOR = vec4( texture(tileTexture, vec2(tc.x, 1.0 - tc.y )).xyz, 1.0 );
+ ROUGHNESS = 0.3;
+ METALNESS = 0.0;
+ FRESNEL_POWER = 1.0;
+}
--- /dev/null
+module OSMBuildings
+Main 1.0 Main.qml
+OSMCameraController 1.0 OSMCameraController.qml
--- /dev/null
+OSM Buildings
+=============
+
+
+This application shows a map obtained from OpenStreetMap (OSM) servers or a
+locally limited data set when the server is unavailable using
+:mod:`Qt Quick 3D <PySide6.QtQuick3D>`.
+
+It is a subset of the equivalent C++ demo, which in addition displays
+buildings. This functionality requires a special license key, though.
+
+Queue handling
+--------------
+
+The application uses a queue to handle concurrent requests to boost up the
+loading process of maps and building data.
+
+Fetching and parsing data
+-------------------------
+
+A custom request handler class is implemented for fetching the data from the
+OSM map servers.
+
+The downloaded ``PNG`` data is sent to a custom ``QQuick3DTextureData`` item to
+convert the ``PNG`` format to a texture for map tiles.
+
+The application uses camera position, orientation, zoom level, and tilt to find
+the nearest tiles in the view.
+
+Controls
+--------
+
+When you run the application, use the following controls for navigation.
+
++---------+---------------------------+---------+
+| | Windows | Android |
++---------+---------------------------+---------+
+| Pan | Left mouse button + drag | Drag |
++---------+---------------------------+---------+
+| Zoom | Mouse wheel | Pinch |
++---------+---------------------------+---------+
+| Rotate | Right mouse button + drag | n/a |
++---------+---------------------------+---------+
+
+Rendering
+---------
+
+Every chunk of the map tile consists of a QML model (the 3D geometry) and a
+custom material which uses a rectangle as a base to render the tilemap texture.
+
+.. image:: osmbuildings.webp
+ :align: center
+ :alt: OSM Buildings Demo
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from pathlib import Path
+
+from PySide6.QtQml import QQmlApplicationEngine
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtCore import QCoreApplication
+
+from manager import OSMManager, CustomTextureData # noqa: F401
+
+
+if __name__ == "__main__":
+ app = QGuiApplication(sys.argv)
+ engine = QQmlApplicationEngine()
+ engine.addImportPath(Path(__file__).parent)
+ engine.loadFromModule("OSMBuildings", "Main")
+ if not engine.rootObjects():
+ sys.exit(-1)
+
+ exit_code = QCoreApplication.exec()
+ del engine
+ sys.exit(exit_code)
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from PySide6.QtQuick3D import QQuick3DTextureData
+from PySide6.QtQml import QmlElement
+from PySide6.QtGui import QImage, QVector3D
+from PySide6.QtCore import QByteArray, QObject, Property, Slot, Signal
+
+from request import OSMTileData, OSMRequest
+
+# To be used on the @QmlElement decorator
+# (QML_IMPORT_MINOR_VERSION is optional)
+QML_IMPORT_NAME = "OSMBuildings"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class OSMManager(QObject):
+
+ mapsDataReady = Signal(QByteArray, int, int, int)
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.m_request = OSMRequest(self)
+ self.m_startBuildingTileX = 17605
+ self.m_startBuildingTileY = 10746
+ self.m_tileSizeX = 37
+ self.m_tileSizeY = 37
+ self.m_request.mapsDataReady.connect(self._slotMapsDataReady)
+
+ def tileSizeX(self):
+ return self.m_tileSizeX
+
+ def tileSizeY(self):
+ return self.m_tileSizeY
+
+ @Slot(QByteArray, int, int, int)
+ def _slotMapsDataReady(self, mapData, tileX, tileY, zoomLevel):
+ self.mapsDataReady.emit(mapData, tileX - self.m_startBuildingTileX,
+ tileY - self.m_startBuildingTileY, zoomLevel)
+
+ @Slot(QVector3D, QVector3D, float, float, float, float, float, float)
+ def setCameraProperties(self, position, right,
+ cameraZoom, minimumZoom, maximumZoom,
+ cameraTilt, minimumTilt, maximumTilt):
+
+ tiltFactor = (cameraTilt - minimumTilt) / max(maximumTilt - minimumTilt, 1.0)
+ zoomFactor = (cameraZoom - minimumZoom) / max(maximumZoom - minimumZoom, 1.0)
+
+ # Forward vector align to the XY plane
+ forwardVector = QVector3D.crossProduct(right, QVector3D(0.0, 0.0, -1.0)).normalized()
+ projectionOfForwardOnXY = position + forwardVector * tiltFactor * zoomFactor * 50.0
+
+ queue = []
+ for forwardIndex in range(-20, 21):
+ for sidewardIndex in range(-20, 21):
+ vx = float(self.m_tileSizeX * sidewardIndex)
+ vy = float(self.m_tileSizeY * forwardIndex)
+ transferredPosition = projectionOfForwardOnXY + QVector3D(vx, vy, 0)
+ tile_x = self.m_startBuildingTileX + int(transferredPosition.x() / self.m_tileSizeX)
+ tile_y = self.m_startBuildingTileY - int(transferredPosition.y() / self.m_tileSizeY)
+ self.addBuildingRequestToQueue(queue, tile_x, tile_y)
+
+ projectedTileX = (self.m_startBuildingTileX + int(projectionOfForwardOnXY.x()
+ / self.m_tileSizeX))
+ projectedTileY = (self.m_startBuildingTileY - int(projectionOfForwardOnXY.y()
+ / self.m_tileSizeY))
+
+ def tile_sort_key(tile_data):
+ return tile_data.distanceTo(projectedTileX, projectedTileY)
+
+ queue.sort(key=tile_sort_key)
+
+ self.m_request.getMapsData(queue.copy())
+
+ def addBuildingRequestToQueue(self, queue, tileX, tileY, zoomLevel=15):
+ queue.append(OSMTileData(tileX, tileY, zoomLevel))
+
+ @Slot(result=bool)
+ def isDemoToken(self):
+ return self.m_request.isDemoToken()
+
+ @Slot(str)
+ def setToken(self, token):
+ self.m_request.setToken(token)
+
+ @Slot(result=str)
+ def token(self):
+ return self.m_request.token()
+
+ tileSizeX = Property(int, tileSizeX, constant=True)
+ tileSizeY = Property(int, tileSizeY, constant=True)
+
+
+@QmlElement
+class CustomTextureData(QQuick3DTextureData):
+
+ @Slot(QByteArray)
+ def setImageData(self, data):
+ image = QImage.fromData(data).convertToFormat(QImage.Format.Format_RGBA8888)
+ self.setTextureData(QByteArray(bytearray(image.constBits())))
+ self.setSize(image.size())
+ self.setHasTransparency(False)
+ self.setFormat(QQuick3DTextureData.Format.RGBA8)
--- /dev/null
+{
+ "files": ["main.py", "manager.py", "request.py",
+ "OSMBuildings/Main.qml", "OSMBuildings/OSMCameraController.qml",
+ "OSMBuildings/customshadertiles.frag"]
+}
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import math
+import sys
+from dataclasses import dataclass
+from functools import partial
+
+from PySide6.QtNetwork import QNetworkAccessManager, QNetworkReply, QNetworkRequest
+from PySide6.QtCore import (QByteArray, QTimer, QFile, QFileInfo,
+ QObject, QUrl, Signal, Slot)
+
+# %1 = zoom level(is dynamic), %2 = x tile number, %3 = y tile number
+URL_OSMB_MAP = "https://tile-a.openstreetmap.fr/hot/{}/{}/{}.png"
+
+
+@dataclass
+class OSMTileData:
+ TileX: int = 0
+ TileY: int = 0
+ ZoomLevel: int = 1
+
+ def distanceTo(self, x, y):
+ deltaX = float(self.TileX) - float(x)
+ deltaY = float(self.TileY) - float(y)
+ return math.sqrt(deltaX * deltaX + deltaY * deltaY)
+
+ def __eq__(self, rhs):
+ return self._equals(rhs)
+
+ def __ne__(self, rhs):
+ return not self._equals(rhs)
+
+ def __hash__(self):
+ return hash((self.TileX, self.TileY, self.ZoomLevel))
+
+ def _equals(self, rhs):
+ return (self.TileX == rhs.TileX and self.TileY == rhs.TileY
+ and self.ZoomLevel == rhs.ZoomLevel)
+
+
+def tileKey(tile):
+ return f"{tile.ZoomLevel},{tile.TileX},{tile.TileY}"
+
+
+class OSMRequest(QObject):
+
+ mapsDataReady = Signal(QByteArray, int, int, int)
+
+ def __init__(self, parent):
+ super().__init__(parent)
+
+ self.m_mapsNumberOfRequestsInFlight = 0
+ self.m_queuesTimer = QTimer()
+ self.m_queuesTimer.setInterval(0)
+ self.m_buildingsQueue = []
+ self.m_mapsQueue = []
+ self.m_networkAccessManager = QNetworkAccessManager()
+ self.m_token = ""
+
+ self.m_queuesTimer.timeout.connect(self._slotTimeOut)
+ self.m_queuesTimer.setInterval(0)
+ self.m_lastBuildingsDataError = ""
+ self.m_lastMapsDataError = ""
+
+ @Slot()
+ def _slotTimeOut(self):
+ if not self.m_buildingsQueue and not self.m_mapsQueue:
+ self.m_queuesTimer.stop()
+ else:
+ numConcurrentRequests = 6
+ if self.m_mapsQueue and self.m_mapsNumberOfRequestsInFlight < numConcurrentRequests:
+ self.getMapsDataRequest(self.m_mapsQueue[0])
+ del self.m_mapsQueue[0]
+
+ self.m_mapsNumberOfRequestsInFlight += 1
+
+ def isDemoToken(self):
+ return not self.m_token
+
+ def token(self):
+ return self.m_token
+
+ def setToken(self, token):
+ self.m_token = token
+
+ def getBuildingsData(self, buildingsQueue):
+ if not buildingsQueue:
+ return
+ self.m_buildingsQueue = buildingsQueue
+ if not self.m_queuesTimer.isActive():
+ self.m_queuesTimer.start()
+
+ def getMapsData(self, mapsQueue):
+ if not mapsQueue:
+ return
+ self.m_mapsQueue = mapsQueue
+ if not self.m_queuesTimer.isActive():
+ self.m_queuesTimer.start()
+
+ def getMapsDataRequest(self, tile):
+ fileName = "data/" + tileKey(tile) + ".png"
+ if QFileInfo.exists(fileName):
+ file = QFile(fileName)
+ if file.open(QFile.OpenModeFlag.ReadOnly):
+ data = file.readAll()
+ file.close()
+ self.mapsDataReady.emit(data, tile.TileX, tile.TileY, tile.ZoomLevel)
+ self.m_mapsNumberOfRequestsInFlight -= 1
+ return
+
+ url = QUrl(URL_OSMB_MAP.format(tile.ZoomLevel, tile.TileX, tile.TileY))
+ reply = self.m_networkAccessManager.get(QNetworkRequest(url))
+ reply.finished.connect(partial(self._mapsDataReceived, reply, tile))
+
+ @Slot(OSMTileData)
+ def _mapsDataReceived(self, reply, tile):
+ reply.deleteLater()
+ if reply.error() == QNetworkReply.NetworkError.NoError:
+ data = reply.readAll()
+ self.mapsDataReady.emit(data, tile.TileX, tile.TileY, tile.ZoomLevel)
+ else:
+ message = reply.readAll().data().decode('utf-8')
+ if message != self.m_lastMapsDataError:
+ self.m_lastMapsDataError = message
+ print("OSMRequest.getMapsDataRequest", reply.error(),
+ reply.url(), message, file=sys.stderr)
+ self.m_mapsNumberOfRequestsInFlight -= 1
-.. _task-menu-extension-example:
-
Task Menu Extension Example
===========================
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Qt Designer taskmenuextension example from Qt v6.x"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from tictactoe import TicTacToe # noqa: F401
from tictactoeplugin import TicTacToePlugin
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Qt, QPoint, QRect, QSize, Property, Slot
from PySide6.QtGui import QPainter, QPen
def paintEvent(self, event):
with QPainter(self) as painter:
- painter.setRenderHint(QPainter.Antialiasing)
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
- painter.setPen(QPen(Qt.darkGreen, 1))
+ painter.setPen(QPen(Qt.GlobalColor.darkGreen, 1))
painter.drawLine(self._cell_width(), 0,
self._cell_width(), self.height())
painter.drawLine(2 * self._cell_width(), 0,
painter.drawLine(0, 2 * self._cell_height(),
self.width(), 2 * self._cell_height())
- painter.setPen(QPen(Qt.darkBlue, 2))
+ painter.setPen(QPen(Qt.GlobalColor.darkBlue, 2))
for position in range(9):
cell = self._cell_rect(position)
elif self._state[position] == NOUGHT:
painter.drawEllipse(cell)
- painter.setPen(QPen(Qt.yellow, 3))
+ painter.setPen(QPen(Qt.GlobalColor.yellow, 3))
for position in range(0, 8, 3):
if (self._state[position] != EMPTY
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from tictactoe import TicTacToe
from tictactoetaskmenu import TicTacToeTaskMenuFactory
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from tictactoe import TicTacToe
layout = QVBoxLayout(self)
self._ticTacToe = TicTacToe(self)
layout.addWidget(self._ticTacToe)
- button_box = QDialogButtonBox(QDialogButtonBox.Ok
- | QDialogButtonBox.Cancel
- | QDialogButtonBox.Reset)
+ button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok
+ | QDialogButtonBox.StandardButton.Cancel
+ | QDialogButtonBox.StandardButton.Reset)
button_box.accepted.connect(self.accept)
button_box.rejected.connect(self.reject)
- reset_button = button_box.button(QDialogButtonBox.Reset)
+ reset_button = button_box.button(QDialogButtonBox.StandardButton.Reset)
reset_button.clicked.connect(self._ticTacToe.clear_board)
layout.addWidget(button_box)
def _edit_state(self):
dialog = TicTacToeDialog(self._ticTacToe)
dialog.set_state(self._ticTacToe.state)
- if dialog.exec() == QDialog.Accepted:
+ if dialog.exec() == QDialog.DialogCode.Accepted:
self._ticTacToe.state = dialog.state()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
self.menu_file.addAction(exit)
self.menu_about = self.menu.addMenu("&About")
- about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents),
+ about = QAction("About Qt", self,
+ shortcut=QKeySequence(QKeySequence.StandardKey.HelpContents),
triggered=qApp.aboutQt) # noqa: F821
self.menu_about.addAction(about)
# Sliders (Left)
min = 0
max = 360
- self.slider_azim = QSlider(minimum=min, maximum=max, orientation=Qt.Horizontal)
- self.slider_elev = QSlider(minimum=min, maximum=max, orientation=Qt.Horizontal)
+ self.slider_azim = QSlider(minimum=min, maximum=max, orientation=Qt.Orientation.Horizontal)
+ self.slider_elev = QSlider(minimum=min, maximum=max, orientation=Qt.Orientation.Horizontal)
self.slider_azim_layout = QHBoxLayout()
self.slider_azim_layout.addWidget(QLabel(f"{min}"))
# Table (Right)
self.table = QTableWidget()
header = self.table.horizontalHeader()
- header.setSectionResizeMode(QHeaderView.Stretch)
+ header.setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
# ComboBox (Right)
self.combo = QComboBox()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import math
self._radius = 30
self._rect = QRectF(0, 0, self._radius * 2, self._radius * 2)
- self.setFlag(QGraphicsItem.ItemIsMovable)
- self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
- self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
+ self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable)
+ self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemSendsGeometryChanges)
+ self.setCacheMode(QGraphicsItem.CacheMode.DeviceCoordinateCache)
def boundingRect(self) -> QRectF:
"""Override from QGraphicsItem
painter (QPainter)
option (QStyleOptionGraphicsItem)
"""
- painter.setRenderHints(QPainter.Antialiasing)
+ painter.setRenderHints(QPainter.RenderHint.Antialiasing)
painter.setPen(
QPen(
QColor(self._color).darker(),
2,
- Qt.SolidLine,
- Qt.RoundCap,
- Qt.RoundJoin,
+ Qt.PenStyle.SolidLine,
+ Qt.PenCapStyle.RoundCap,
+ Qt.PenJoinStyle.RoundJoin,
)
)
painter.setBrush(QBrush(QColor(self._color)))
painter.drawEllipse(self.boundingRect())
painter.setPen(QPen(QColor("white")))
- painter.drawText(self.boundingRect(), Qt.AlignCenter, self._name)
+ painter.drawText(self.boundingRect(), Qt.AlignmentFlag.AlignCenter, self._name)
def add_edge(self, edge):
"""Add an edge to this node
Returns:
Any
"""
- if change == QGraphicsItem.ItemPositionHasChanged:
+ if change == QGraphicsItem.GraphicsItemChange.ItemPositionHasChanged:
for edge in self._edges:
edge.adjust()
"""
if self._source and self._dest:
- painter.setRenderHints(QPainter.Antialiasing)
+ painter.setRenderHints(QPainter.RenderHint.Antialiasing)
painter.setPen(
QPen(
QColor(self._color),
self._tickness,
- Qt.SolidLine,
- Qt.RoundCap,
- Qt.RoundJoin,
+ Qt.PenStyle.SolidLine,
+ Qt.PenCapStyle.RoundCap,
+ Qt.PenJoinStyle.RoundJoin,
)
)
painter.drawLine(self._line)
animation = QPropertyAnimation(item, b"pos")
animation.setDuration(1000)
animation.setEndValue(QPointF(x, y))
- animation.setEasingCurve(QEasingCurve.OutExpo)
+ animation.setEasingCurve(QEasingCurve.Type.OutExpo)
self.animations.addAnimation(animation)
self.animations.start()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
import sys
# Creating and scaling QImage
h, w, ch = color_frame.shape
- img = QImage(color_frame.data, w, h, ch * w, QImage.Format_RGB888)
- scaled_img = img.scaled(640, 480, Qt.KeepAspectRatio)
+ img = QImage(color_frame.data, w, h, ch * w, QImage.Format.Format_RGB888)
+ scaled_img = img.scaled(640, 480, Qt.AspectRatioMode.KeepAspectRatio)
# Emit signal
self.updateFrame.emit(scaled_img)
self.menu_file.addAction(exit)
self.menu_about = self.menu.addMenu("&About")
- about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents),
+ about = QAction("About Qt", self,
+ shortcut=QKeySequence(QKeySequence.StandardKey.HelpContents),
triggered=qApp.aboutQt) # noqa: F821
self.menu_about.addAction(about)
# Model group
self.group_model = QGroupBox("Trained model")
- self.group_model.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
+ self.group_model.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
model_layout = QHBoxLayout()
self.combobox = QComboBox()
buttons_layout = QHBoxLayout()
self.button1 = QPushButton("Start")
self.button2 = QPushButton("Stop/Close")
- self.button1.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
- self.button2.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
+ self.button1.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
+ self.button2.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
buttons_layout.addWidget(self.button2)
buttons_layout.addWidget(self.button1)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import pandas as pd
if not index.isValid():
return None
- if role == Qt.DisplayRole:
+ if role == Qt.ItemDataRole.DisplayRole:
return str(self._dataframe.iloc[index.row(), index.column()])
return None
Return dataframe index as vertical header data and columns as horizontal header data.
"""
- if role == Qt.DisplayRole:
- if orientation == Qt.Horizontal:
+ if role == Qt.ItemDataRole.DisplayRole:
+ if orientation == Qt.Orientation.Horizontal:
return str(self._dataframe.columns[section])
if orientation == Qt.Vertical:
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
GraphsView {
anchors.fill: parent
anchors.margins: 16
- theme: GraphTheme {
- colorTheme: GraphTheme.ColorThemeDark
+ axisX: BarCategoryAxis {
+ categories: [2024, 2025, 2026]
+ gridVisible: false
+ subGridVisible: false
+ }
+ axisY: ValueAxis {
+ min: 20
+ max: 100
+ tickInterval: 10
+ subTickCount: 9
+ }
+ theme: GraphsTheme {
+ colorScheme: Qt.Dark
+ theme: GraphsTheme.Theme.QtGreen
}
//! [bargraph]
//! [barseries]
BarSeries {
- axisX: BarCategoryAxis {
- categories: [2024, 2025, 2026]
- gridVisible: false
- minorGridVisible: false
- }
- axisY: ValueAxis {
- min: 20
- max: 100
- tickInterval: 10
- minorTickCount: 9
- }
//! [barseries]
//! [barset]
BarSet {
GraphsView {
anchors.fill: parent
anchors.margins: 16
- theme: GraphTheme {
+ theme: GraphsTheme {
readonly property color c1: "#DBEB00"
readonly property color c2: "#373F26"
readonly property color c3: Qt.lighter(c2, 1.5)
- colorTheme: GraphTheme.ColorThemeDark
- gridMajorBarsColor: c3
- gridMinorBarsColor: c2
- axisXMajorColor: c3
- axisYMajorColor: c3
- axisXMinorColor: c2
- axisYMinorColor: c2
- axisXLabelsColor: c1
- axisYLabelsColor: c1
+ colorScheme: Qt.Dark
+ seriesColors: ["#2CDE85", "#DBEB00"]
+ grid.mainColor: c3
+ grid.subColor: c2
+ axisX.mainColor: c3
+ axisY.mainColor: c3
+ axisX.subColor: c2
+ axisY.subColor: c2
+ axisX.labelTextColor: c1
+ axisY.labelTextColor: c1
+ }
+ axisX: ValueAxis {
+ max: 5
+ tickInterval: 1
+ subTickCount: 9
+ labelDecimals: 1
+ }
+ axisY: ValueAxis {
+ max: 10
+ tickInterval: 1
+ subTickCount: 4
+ labelDecimals: 1
}
//! [linegraph]
}
//! [linemarker]
- //! [lineseriestheme]
- SeriesTheme {
- id: seriesTheme
- colors: ["#2CDE85", "#DBEB00"]
- }
- //! [lineseriestheme]
-
//! [lineseries1]
LineSeries {
id: lineSeries1
- theme: seriesTheme
- axisX: ValueAxis {
- max: 5
- tickInterval: 1
- minorTickCount: 9
- labelDecimals: 1
- }
- axisY: ValueAxis {
- max: 10
- tickInterval: 1
- minorTickCount: 4
- labelDecimals: 1
- }
width: 4
- pointMarker: Marker { }
+ pointDelegate: Marker { }
XYPoint { x: 0; y: 0 }
XYPoint { x: 1; y: 2.1 }
XYPoint { x: 2; y: 3.3 }
//! [lineseries2]
LineSeries {
id: lineSeries2
- theme: seriesTheme
width: 4
- pointMarker: Marker { }
+ pointDelegate: Marker { }
XYPoint { x: 0; y: 5.0 }
XYPoint { x: 1; y: 3.3 }
XYPoint { x: 2; y: 7.1 }
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Qt Hello Graphs example from Qt v6.x"""
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtGraphs
+
+Item {
+ property alias column: columnAxis
+ property alias row: rowAxis
+ property alias value: valueAxis
+ property alias total: totalAxis
+
+ // Custom labels for columns, since the data contains abbreviated month names.
+ //! [0]
+ Category3DAxis {
+ id: columnAxis
+ labels: ["January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"]
+ labelAutoAngle: 30
+ }
+ //! [0]
+ Category3DAxis {
+ id: totalAxis
+ labels: ["Yearly total"]
+ labelAutoAngle: 30
+ }
+ Category3DAxis {
+ // For row labels we can use row labels from data proxy, no labels defined for rows.
+ id: rowAxis
+ labelAutoAngle: 30
+ }
+
+ Value3DAxis {
+ id: valueAxis
+ min: 0
+ max: 35
+ labelFormat: "%.2f M\u20AC"
+ title: "Monthly income"
+ labelAutoAngle: 90
+ }
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQml.Models
+
+Item {
+ property alias model: dataModel
+
+ property var modelAsJsArray: {
+ var arr = [];
+ for (var i = 0; i < dataModel.count; i++) {
+ var row = dataModel.get(i);
+ arr.push({
+ timestamp: row.timestamp,
+ expenses: row.expenses,
+ income: row.income
+ });
+ }
+ return arr;
+ }
+
+ //! [0]
+ ListModel {
+ id: dataModel
+ ListElement{ timestamp: "2016-01"; expenses: "-4"; income: "5" }
+ ListElement{ timestamp: "2016-02"; expenses: "-5"; income: "6" }
+ ListElement{ timestamp: "2016-03"; expenses: "-7"; income: "4" }
+ //! [0]
+ ListElement{ timestamp: "2016-04"; expenses: "-3"; income: "2" }
+ ListElement{ timestamp: "2016-05"; expenses: "-4"; income: "1" }
+ ListElement{ timestamp: "2016-06"; expenses: "-2"; income: "2" }
+ ListElement{ timestamp: "2016-07"; expenses: "-1"; income: "3" }
+ ListElement{ timestamp: "2016-08"; expenses: "-5"; income: "1" }
+ ListElement{ timestamp: "2016-09"; expenses: "-2"; income: "3" }
+ ListElement{ timestamp: "2016-10"; expenses: "-5"; income: "2" }
+ ListElement{ timestamp: "2016-11"; expenses: "-8"; income: "5" }
+ ListElement{ timestamp: "2016-12"; expenses: "-3"; income: "3" }
+
+ ListElement{ timestamp: "2017-01"; expenses: "-3"; income: "1" }
+ ListElement{ timestamp: "2017-02"; expenses: "-4"; income: "2" }
+ ListElement{ timestamp: "2017-03"; expenses: "-12"; income: "4" }
+ ListElement{ timestamp: "2017-04"; expenses: "-13"; income: "6" }
+ ListElement{ timestamp: "2017-05"; expenses: "-14"; income: "11" }
+ ListElement{ timestamp: "2017-06"; expenses: "-7"; income: "7" }
+ ListElement{ timestamp: "2017-07"; expenses: "-6"; income: "4" }
+ ListElement{ timestamp: "2017-08"; expenses: "-4"; income: "15" }
+ ListElement{ timestamp: "2017-09"; expenses: "-2"; income: "18" }
+ ListElement{ timestamp: "2017-10"; expenses: "-29"; income: "25" }
+ ListElement{ timestamp: "2017-11"; expenses: "-23"; income: "29" }
+ ListElement{ timestamp: "2017-12"; expenses: "-5"; income: "9" }
+
+ ListElement{ timestamp: "2018-01"; expenses: "-3"; income: "8" }
+ ListElement{ timestamp: "2018-02"; expenses: "-8"; income: "14" }
+ ListElement{ timestamp: "2018-03"; expenses: "-10"; income: "20" }
+ ListElement{ timestamp: "2018-04"; expenses: "-12"; income: "24" }
+ ListElement{ timestamp: "2018-05"; expenses: "-10"; income: "19" }
+ ListElement{ timestamp: "2018-06"; expenses: "-5"; income: "8" }
+ ListElement{ timestamp: "2018-07"; expenses: "-1"; income: "4" }
+ ListElement{ timestamp: "2018-08"; expenses: "-7"; income: "12" }
+ ListElement{ timestamp: "2018-09"; expenses: "-4"; income: "16" }
+ ListElement{ timestamp: "2018-10"; expenses: "-22"; income: "33" }
+ ListElement{ timestamp: "2018-11"; expenses: "-16"; income: "25" }
+ ListElement{ timestamp: "2018-12"; expenses: "-2"; income: "7" }
+
+ ListElement{ timestamp: "2019-01"; expenses: "-4"; income: "5" }
+ ListElement{ timestamp: "2019-02"; expenses: "-4"; income: "7" }
+ ListElement{ timestamp: "2019-03"; expenses: "-11"; income: "14" }
+ ListElement{ timestamp: "2019-04"; expenses: "-16"; income: "22" }
+ ListElement{ timestamp: "2019-05"; expenses: "-3"; income: "5" }
+ ListElement{ timestamp: "2019-06"; expenses: "-4"; income: "8" }
+ ListElement{ timestamp: "2019-07"; expenses: "-7"; income: "9" }
+ ListElement{ timestamp: "2019-08"; expenses: "-9"; income: "13" }
+ ListElement{ timestamp: "2019-09"; expenses: "-1"; income: "6" }
+ ListElement{ timestamp: "2019-10"; expenses: "-14"; income: "25" }
+ ListElement{ timestamp: "2019-11"; expenses: "-19"; income: "29" }
+ ListElement{ timestamp: "2019-12"; expenses: "-5"; income: "7" }
+
+ ListElement{ timestamp: "2020-01"; expenses: "-14"; income: "22" }
+ ListElement{ timestamp: "2020-02"; expenses: "-5"; income: "7" }
+ ListElement{ timestamp: "2020-03"; expenses: "-1"; income: "9" }
+ ListElement{ timestamp: "2020-04"; expenses: "-1"; income: "12" }
+ ListElement{ timestamp: "2020-05"; expenses: "-5"; income: "9" }
+ ListElement{ timestamp: "2020-06"; expenses: "-5"; income: "8" }
+ ListElement{ timestamp: "2020-07"; expenses: "-3"; income: "7" }
+ ListElement{ timestamp: "2020-08"; expenses: "-1"; income: "5" }
+ ListElement{ timestamp: "2020-09"; expenses: "-2"; income: "4" }
+ ListElement{ timestamp: "2020-10"; expenses: "-10"; income: "13" }
+ ListElement{ timestamp: "2020-11"; expenses: "-12"; income: "17" }
+ ListElement{ timestamp: "2020-12"; expenses: "-6"; income: "9" }
+
+ ListElement{ timestamp: "2021-01"; expenses: "-2"; income: "6" }
+ ListElement{ timestamp: "2021-02"; expenses: "-4"; income: "8" }
+ ListElement{ timestamp: "2021-03"; expenses: "-7"; income: "12" }
+ ListElement{ timestamp: "2021-04"; expenses: "-9"; income: "15" }
+ ListElement{ timestamp: "2021-05"; expenses: "-7"; income: "19" }
+ ListElement{ timestamp: "2021-06"; expenses: "-9"; income: "18" }
+ ListElement{ timestamp: "2021-07"; expenses: "-13"; income: "17" }
+ ListElement{ timestamp: "2021-08"; expenses: "-5"; income: "9" }
+ ListElement{ timestamp: "2021-09"; expenses: "-3"; income: "8" }
+ ListElement{ timestamp: "2021-10"; expenses: "-13"; income: "15" }
+ ListElement{ timestamp: "2021-11"; expenses: "-8"; income: "17" }
+ ListElement{ timestamp: "2021-12"; expenses: "-7"; income: "10" }
+
+ ListElement{ timestamp: "2022-01"; expenses: "-12"; income: "16" }
+ ListElement{ timestamp: "2022-02"; expenses: "-24"; income: "28" }
+ ListElement{ timestamp: "2022-03"; expenses: "-27"; income: "22" }
+ ListElement{ timestamp: "2022-04"; expenses: "-29"; income: "25" }
+ ListElement{ timestamp: "2022-05"; expenses: "-27"; income: "29" }
+ ListElement{ timestamp: "2022-06"; expenses: "-19"; income: "18" }
+ ListElement{ timestamp: "2022-07"; expenses: "-13"; income: "17" }
+ ListElement{ timestamp: "2022-08"; expenses: "-15"; income: "19" }
+ ListElement{ timestamp: "2022-09"; expenses: "-3"; income: "8" }
+ ListElement{ timestamp: "2022-10"; expenses: "-3"; income: "6" }
+ ListElement{ timestamp: "2022-11"; expenses: "-4"; income: "8" }
+ ListElement{ timestamp: "2022-12"; expenses: "-5"; income: "9" }
+ }
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls.Basic
+import QtQuick.Layouts
+import QtGraphs
+import Qt.labs.qmlmodels
+
+pragma ComponentBehavior: Bound
+
+Item {
+ id: mainview
+ width: 1280
+ height: 1024
+
+ property int buttonLayoutHeight: 180
+ property int currentRow
+ state: Screen.width < Screen.height ? "portrait" : "landscape"
+
+ Data {
+ id: graphData
+ }
+
+ Axes {
+ id: graphAxes
+ }
+
+ property Bar3DSeries selectedSeries
+ selectedSeries: barSeries
+
+ function handleSelectionChange(series, position) {
+ if (position !== series.invalidSelectionPosition)
+ selectedSeries = series
+
+ // Set tableView current row to selected bar
+ var rowRole = series.rowLabels[position.x]
+ var colRole
+ if (barGraph.columnAxis == graphAxes.total)
+ colRole = "01"
+ else
+ colRole = series.columnLabels[position.y]
+ var checkTimestamp = rowRole + "-" + colRole
+
+ if (currentRow === -1 || checkTimestamp !== graphData.model.get(currentRow).timestamp) {
+ var totalRows = tableView.rows
+ for (var i = 0; i < totalRows; i++) {
+ var modelTimestamp = graphData.model.get(i).timestamp
+ if (modelTimestamp === checkTimestamp) {
+ currentRow = i
+ break
+ }
+ }
+ }
+ }
+
+ ColumnLayout {
+ id: tableViewLayout
+
+ anchors.top: parent.top
+ anchors.left: parent.left
+
+ HorizontalHeaderView {
+ id: headerView
+ readonly property var columnNames: ["Month", "Expenses", "Income"]
+
+ syncView: tableView
+ Layout.fillWidth: true
+ delegate: Text {
+ required property int index
+ padding: 3
+ text: headerView.columnNames[index]
+ color: barGraph.theme.labelTextColor
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+ }
+
+ TableView {
+ id: tableView
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ reuseItems: false
+ clip: true
+
+ model: TableModel {
+ id: tableModel
+ TableModelColumn { display: "timestamp" }
+ TableModelColumn { display: "expenses" }
+ TableModelColumn { display: "income" }
+
+ rows: graphData.modelAsJsArray
+ }
+
+ delegate: Rectangle {
+ id: delegateRoot
+ required property int row
+ required property int column
+ required property string display
+ implicitHeight: 30
+ implicitWidth: column === 0 ? tableView.width / 2 : tableView.width / 4
+ color: row === mainview.currentRow ? barGraph.theme.grid.mainColor
+ : barGraph.theme.backgroundColor
+ border.color: row === mainview.currentRow ? barGraph.theme.labelTextColor
+ : barGraph.theme.grid.mainColor
+ border.width: 1
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ mainview.currentRow = delegateRoot.row
+
+ //! [2]
+ var timestamp = graphData.model.get(mainview.currentRow).timestamp
+ var pattern = /(\d\d\d\d)-(\d\d)/
+ var matches = pattern.exec(timestamp)
+ var rowIndex = modelProxy.rowCategoryIndex(matches[1])
+ var colIndex
+
+ if (barGraph.columnAxis == graphAxes.total)
+ colIndex = 0 // Just one column when showing yearly totals
+ else
+ colIndex = modelProxy.columnCategoryIndex(matches[2])
+
+ if (selectedSeries.visible)
+ mainview.selectedSeries.selectedBar = Qt.point(rowIndex, colIndex)
+ else if (barSeries.visible)
+ barSeries.selectedBar = Qt.point(rowIndex, colIndex)
+ else
+ secondarySeries.selectedBar = Qt.point(rowIndex, colIndex)
+ //! [2]
+ }
+ }
+
+ Text {
+ id: delegateText
+ anchors.verticalCenter: parent.verticalCenter
+ width: parent.width
+ anchors.leftMargin: 4
+ anchors.left: parent.left
+ anchors.right: parent.right
+ text: formattedText
+ property string formattedText: {
+ if (delegateRoot.column === 0) {
+ if (delegateRoot.display !== "") {
+ var pattern = /(\d\d\d\d)-(\d\d)/
+ var matches = pattern.exec(delegateRoot.display)
+ var colIndex = parseInt(matches[2], 10) - 1
+ return matches[1] + " - " + graphAxes.column.labels[colIndex]
+ }
+ } else {
+ return delegateRoot.display
+ }
+ }
+ color: delegateRoot.row === mainview.currentRow ? barGraph.theme.backgroundColor
+ : barGraph.theme.labelTextColor
+ horizontalAlignment: delegateRoot.column === 0 ? Text.AlignLeft
+ : Text.AlignHCenter
+ elide: Text.ElideRight
+ }
+ }
+ }
+ }
+
+ ColumnLayout {
+ id: controlLayout
+ spacing: 0
+
+ Button {
+ id: changeDataButton
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ text: "Show 2020 - 2022"
+ clip: true
+ //! [1]
+ onClicked: {
+ if (text === "Show yearly totals") {
+ modelProxy.autoRowCategories = true
+ secondaryProxy.autoRowCategories = true
+ modelProxy.columnRolePattern = /^.*$/
+ secondaryProxy.columnRolePattern = /^.*$/
+ graphAxes.value.autoAdjustRange = true
+ barGraph.columnAxis = graphAxes.total
+ text = "Show all years"
+ } else if (text === "Show all years") {
+ modelProxy.autoRowCategories = true
+ secondaryProxy.autoRowCategories = true
+ modelProxy.columnRolePattern = /^.*-(\d\d)$/
+ secondaryProxy.columnRolePattern = /^.*-(\d\d)$/
+ graphAxes.value.min = 0
+ graphAxes.value.max = 35
+ barGraph.columnAxis = graphAxes.column
+ text = "Show 2020 - 2022"
+ } else { // text === "Show 2020 - 2022"
+ // Explicitly defining row categories, since we do not want to show data for
+ // all years in the model, just for the selected ones.
+ modelProxy.autoRowCategories = false
+ secondaryProxy.autoRowCategories = false
+ modelProxy.rowCategories = ["2020", "2021", "2022"]
+ secondaryProxy.rowCategories = ["2020", "2021", "2022"]
+ text = "Show yearly totals"
+ }
+ }
+ //! [1]
+
+ contentItem: Text {
+ text: changeDataButton.text
+ opacity: changeDataButton.enabled ? 1.0 : 0.3
+ color: barGraph.theme.labelTextColor
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+
+ background: Rectangle {
+ opacity: changeDataButton.enabled ? 1 : 0.3
+ color: changeDataButton.down ? barGraph.theme.grid.mainColor : barGraph.theme.backgroundColor
+ border.color: changeDataButton.down ? barGraph.theme.labelTextColor : barGraph.theme.grid.mainColor
+ border.width: 1
+ radius: 2
+ }
+ }
+
+ Button {
+ id: shadowToggle
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ text: "Hide Shadows"
+ clip: true
+ onClicked: {
+ if (barGraph.shadowQuality == Graphs3D.ShadowQuality.None) {
+ barGraph.shadowQuality = Graphs3D.ShadowQuality.SoftHigh
+ text = "Hide Shadows"
+ } else {
+ barGraph.shadowQuality = Graphs3D.ShadowQuality.None
+ text = "Show Shadows"
+ }
+ }
+ contentItem: Text {
+ text: shadowToggle.text
+ opacity: shadowToggle.enabled ? 1.0 : 0.3
+ color: barGraph.theme.labelTextColor
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+
+ background: Rectangle {
+ opacity: shadowToggle.enabled ? 1 : 0.3
+ color: shadowToggle.down ? barGraph.theme.grid.mainColor : barGraph.theme.backgroundColor
+ border.color: shadowToggle.down ? barGraph.theme.labelTextColor : barGraph.theme.grid.mainColor
+ border.width: 1
+ radius: 2
+ }
+ }
+
+ Button {
+ id: seriesToggle
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ text: "Show Expenses"
+ clip: true
+ //! [0]
+ onClicked: {
+ if (text === "Show Expenses") {
+ barSeries.visible = false
+ secondarySeries.visible = true
+ barGraph.valueAxis.labelFormat = "-%.2f M\u20AC"
+ secondarySeries.itemLabelFormat = "Expenses, @colLabel, @rowLabel: @valueLabel"
+ text = "Show Both"
+ } else if (text === "Show Both") {
+ barSeries.visible = true
+ barGraph.valueAxis.labelFormat = "%.2f M\u20AC"
+ secondarySeries.itemLabelFormat = "Expenses, @colLabel, @rowLabel: -@valueLabel"
+ text = "Show Income"
+ } else { // text === "Show Income"
+ secondarySeries.visible = false
+ text = "Show Expenses"
+ }
+ }
+ //! [0]
+ contentItem: Text {
+ text: seriesToggle.text
+ opacity: seriesToggle.enabled ? 1.0 : 0.3
+ color: barGraph.theme.labelTextColor
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+
+ background: Rectangle {
+ opacity: seriesToggle.enabled ? 1 : 0.3
+ color: seriesToggle.down ? barGraph.theme.grid.mainColor : barGraph.theme.backgroundColor
+ border.color: seriesToggle.down ? barGraph.theme.labelTextColor : barGraph.theme.grid.mainColor
+ border.width: 1
+ radius: 2
+ }
+ }
+
+ Button {
+ id: marginToggle
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ text: "Use Margin"
+ clip: true
+
+ onClicked: {
+ if (text === "Use Margin") {
+ barGraph.barSeriesMargin = Qt.size(0.2, 0.2)
+ barGraph.barSpacing = Qt.size(0.0, 0.0)
+ text = "Use Spacing"
+ } else if (text === "Use Spacing") {
+ barGraph.barSeriesMargin = Qt.size(0.0, 0.0)
+ barGraph.barSpacing = Qt.size(0.5, 0.5)
+ text = "Use Margin"
+ }
+ }
+ contentItem: Text {
+ text: marginToggle.text
+ opacity: marginToggle.enabled ? 1.0 : 0.3
+ color: barGraph.theme.labelTextColor
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
+
+ background: Rectangle {
+ opacity: marginToggle.enabled ? 1 : 0.3
+ color: marginToggle.down ? barGraph.theme.grid.mainColor : barGraph.theme.backgroundColor
+ border.color: marginToggle.down ? barGraph.theme.labelTextColor : barGraph.theme.grid.mainColor
+ border.width: 1
+ radius: 2
+ }
+ }
+ }
+
+ Item {
+ id: dataView
+ anchors.right: mainview.right
+ anchors.bottom: mainview.bottom
+
+ Bars3D {
+ id: barGraph
+ anchors.fill: parent
+ shadowQuality: Graphs3D.ShadowQuality.SoftHigh
+ selectionMode: Graphs3D.SelectionFlag.Item
+ theme: GraphsTheme {
+ colorScheme: GraphsTheme.ColorScheme.Dark
+ labelBorderVisible: true
+ labelFont.pointSize: 35
+ labelBackgroundVisible: true
+ colorStyle: GraphsTheme.ColorStyle.RangeGradient
+ singleHighlightGradient: customGradient
+
+ Gradient {
+ id: customGradient
+ GradientStop { position: 1.0; color: "#FFFF00" }
+ GradientStop { position: 0.0; color: "#808000" }
+ }
+ }
+ barThickness: 0.7
+ barSpacing: Qt.size(0.5, 0.5)
+ barSpacingRelative: false
+ cameraPreset: Graphs3D.CameraPreset.IsometricLeftHigh
+ columnAxis: graphAxes.column
+ rowAxis: graphAxes.row
+ valueAxis: graphAxes.value
+
+ //! [4]
+ Bar3DSeries {
+ id: secondarySeries
+ visible: false
+ itemLabelFormat: "Expenses, @colLabel, @rowLabel: -@valueLabel"
+ baseGradient: secondaryGradient
+
+ ItemModelBarDataProxy {
+ id: secondaryProxy
+ itemModel: graphData.model
+ rowRole: "timestamp"
+ columnRole: "timestamp"
+ valueRole: "expenses"
+ rowRolePattern: /^(\d\d\d\d).*$/
+ columnRolePattern: /^.*-(\d\d)$/
+ valueRolePattern: /-/
+ rowRoleReplace: "\\1"
+ columnRoleReplace: "\\1"
+ multiMatchBehavior: ItemModelBarDataProxy.MultiMatchBehavior.Cumulative
+ }
+ //! [4]
+
+ Gradient {
+ id: secondaryGradient
+ GradientStop { position: 1.0; color: "#FF0000" }
+ GradientStop { position: 0.0; color: "#600000" }
+ }
+
+ onSelectedBarChanged: (position) => mainview.handleSelectionChange(secondarySeries,
+ position)
+ }
+
+ //! [3]
+ Bar3DSeries {
+ id: barSeries
+ itemLabelFormat: "Income, @colLabel, @rowLabel: @valueLabel"
+ baseGradient: barGradient
+
+ ItemModelBarDataProxy {
+ id: modelProxy
+ itemModel: graphData.model
+ rowRole: "timestamp"
+ columnRole: "timestamp"
+ valueRole: "income"
+ rowRolePattern: /^(\d\d\d\d).*$/
+ columnRolePattern: /^.*-(\d\d)$/
+ rowRoleReplace: "\\1"
+ columnRoleReplace: "\\1"
+ multiMatchBehavior: ItemModelBarDataProxy.MultiMatchBehavior.Cumulative
+ }
+ //! [3]
+
+ Gradient {
+ id: barGradient
+ GradientStop { position: 1.0; color: "#00FF00" }
+ GradientStop { position: 0.0; color: "#006000" }
+ }
+
+ onSelectedBarChanged: (position) => mainview.handleSelectionChange(barSeries,
+ position)
+ }
+ }
+ }
+
+ states: [
+ State {
+ name: "landscape"
+ PropertyChanges {
+ target: dataView
+ width: mainview.width / 4 * 3
+ height: mainview.height
+ }
+ PropertyChanges {
+ target: tableViewLayout
+ height: mainview.height - buttonLayoutHeight
+ anchors.right: dataView.left
+ anchors.left: mainview.left
+ anchors.bottom: undefined
+ }
+ PropertyChanges {
+ target: controlLayout
+ width: mainview.width / 4
+ height: buttonLayoutHeight
+ anchors.top: tableViewLayout.bottom
+ anchors.bottom: mainview.bottom
+ anchors.left: mainview.left
+ anchors.right: dataView.left
+ }
+ },
+ State {
+ name: "portrait"
+ PropertyChanges {
+ target: dataView
+ width: mainview.width
+ height: mainview.width
+ }
+ PropertyChanges {
+ target: tableViewLayout
+ height: mainview.width
+ anchors.right: controlLayout.left
+ anchors.left: mainview.left
+ anchors.bottom: dataView.top
+ }
+ PropertyChanges {
+ target: controlLayout
+ width: mainview.height / 4
+ height: mainview.width / 4
+ anchors.top: mainview.top
+ anchors.bottom: dataView.top
+ anchors.left: undefined
+ anchors.right: mainview.right
+ }
+ }
+ ]
+}
--- /dev/null
+module Bars
+Main 1.0 Main.qml
+Axes 1.0 Axes.qml
+Data 1.0 Data.qml
--- /dev/null
+{
+ "files": [
+ "main.py",
+ "Bars/Main.qml",
+ "Bars/Data.qml",
+ "Bars/Axes.qml",
+ "Bars/qmldir"
+ ]
+}
--- /dev/null
+Simple Bar Graph
+================
+
+The Qt 3D Bar Graph example demonstrates creating a 3D bar graph in QML
+using Bars3D. It visualizes fictional company data for income and expenses
+over time, showcasing features like data series switching, custom axis labels,
+and interactive data selection.
+
+
+.. image:: bars-example.webp
+ :width: 400
+ :alt: Widget Screenshot
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from pathlib import Path
+
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQuick import QQuickView
+
+
+if __name__ == '__main__':
+ app = QGuiApplication(sys.argv)
+ view = QQuickView()
+
+ view.engine().addImportPath(Path(__file__).parent)
+ view.loadFromModule("Bars", "Main")
+ view.setTitle("Monthly income / expenses")
+ view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
+ view.setColor("black")
+ view.show()
+
+ ex = app.exec()
+ del view
+ sys.exit(ex)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtCore import QSize
from PySide6.QtGui import QVector3D
-from PySide6.QtGraphs import (Q3DSurface, QSurfaceDataItem,
- QSurface3DSeries)
-from PySide6.QtWidgets import QApplication
+from PySide6.QtGraphs import (QSurfaceDataItem, QSurface3DSeries)
+from PySide6.QtGraphsWidgets import (Q3DSurfaceWidgetItem)
from PySide6.QtQuickWidgets import QQuickWidget
+from PySide6.QtWidgets import QApplication
DESCRIPTION = """Minimal Qt Graphs Surface Example
print(DESCRIPTION)
- surface = Q3DSurface()
+ window = QQuickWidget()
+ surface = Q3DSurfaceWidgetItem()
+ surface.setWidget(window)
axis = surface.axisX()
axis.setTitle("X")
axis.setTitleVisible(True)
available_height = app.primaryScreen().availableGeometry().height()
width = available_height * 4 / 5
- surface.resize(QSize(width, width))
- surface.setResizeMode(QQuickWidget.SizeRootObjectToView)
- surface.show()
+ window.resize(QSize(width, width))
+ window.show()
- sys.exit(app.exec())
+ exit_code = app.exec()
+ surface = None
+ del window
+ sys.exit(exit_code)
--- /dev/null
+{
+ "files": ["main.py"]
+}
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from enum import Enum
-from math import sin, cos, degrees
-
-from PySide6.QtCore import Qt
-from PySide6.QtGraphs import QAbstract3DGraph, Q3DInputHandler
-
-
-class InputState(Enum):
- StateNormal = 0
- StateDraggingX = 1
- StateDraggingZ = 2
- StateDraggingY = 3
-
-
-class AxesInputHandler(Q3DInputHandler):
-
- def __init__(self, graph, parent=None):
- super().__init__(parent)
- self._mousePressed = False
- self._state = InputState.StateNormal
- self._axisX = None
- self._axisZ = None
- self._axisY = None
- self._speedModifier = 15.0
-
- # Connect to the item selection signal from graph
- graph.selectedElementChanged.connect(self.handleElementSelected)
-
- def setAxes(self, axisX, axisZ, axisY):
- self._axisX = axisX
- self._axisZ = axisZ
- self._axisY = axisY
-
- def setDragSpeedModifier(self, modifier):
- self._speedModifier = modifier
-
- def mousePressEvent(self, event, mousePos):
- super().mousePressEvent(event, mousePos)
- if Qt.LeftButton == event.button():
- self._mousePressed = True
-
- def mouseMoveEvent(self, event, mousePos):
- # Check if we're trying to drag axis label
- if self._mousePressed and self._state != InputState.StateNormal:
- self.setPreviousInputPos(self.inputPosition())
- self.setInputPosition(mousePos)
- self.handleAxisDragging()
- else:
- super().mouseMoveEvent(event, mousePos)
-
- def mouseReleaseEvent(self, event, mousePos):
- super().mouseReleaseEvent(event, mousePos)
- self._mousePressed = False
- self._state = InputState.StateNormal
-
- def handleElementSelected(self, type):
- if type == QAbstract3DGraph.ElementAxisXLabel:
- self._state = InputState.StateDraggingX
- elif type == QAbstract3DGraph.ElementAxisYLabel:
- self._state = InputState.StateDraggingY
- elif type == QAbstract3DGraph.ElementAxisZLabel:
- self._state = InputState.StateDraggingZ
- else:
- self._state = InputState.StateNormal
-
- def handleAxisDragging(self):
- distance = 0.0
- # Get scene orientation from active camera
- xRotation = self.cameraXRotation()
- yRotation = self.cameraYRotation()
-
- # Calculate directional drag multipliers based on rotation
- xMulX = cos(degrees(xRotation))
- xMulY = sin(degrees(xRotation))
- zMulX = sin(degrees(xRotation))
- zMulY = cos(degrees(xRotation))
-
- # Get the drag amount
- move = self.inputPosition() - self.previousInputPos()
-
- # Flip the effect of y movement if we're viewing from below
- yMove = -move.y() if yRotation < 0 else move.y()
-
- # Adjust axes
- if self._state == InputState.StateDraggingX:
- distance = (move.x() * xMulX - yMove * xMulY) / self._speedModifier
- self._axisX.setRange(self._axisX.min() - distance,
- self._axisX.max() - distance)
- elif self._state == InputState.StateDraggingZ:
- distance = (move.x() * zMulX + yMove * zMulY) / self._speedModifier
- self._axisZ.setRange(self._axisZ.min() + distance,
- self._axisZ.max() + distance)
- elif self._state == InputState.StateDraggingY:
- # No need to use adjusted y move here
- distance = move.y() / self._speedModifier
- self._axisY.setRange(self._axisY.min() + distance,
- self._axisY.max() + distance)
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from graphmodifier import GraphModifier
-
-from PySide6.QtCore import QObject, Qt
-from PySide6.QtGui import QFont
-from PySide6.QtWidgets import (QButtonGroup, QCheckBox, QComboBox, QFontComboBox,
- QLabel, QPushButton, QHBoxLayout, QSizePolicy,
- QRadioButton, QSlider, QVBoxLayout, QWidget)
-from PySide6.QtQuickWidgets import QQuickWidget
-from PySide6.QtGraphs import (QAbstract3DGraph, QAbstract3DSeries, Q3DBars)
-
-
-class BarGraph(QObject):
-
- def __init__(self, minimum_graph_size, maximum_graph_size):
- super().__init__()
- self._barsGraph = Q3DBars()
- self._barsWidget = QWidget()
- hLayout = QHBoxLayout(self._barsWidget)
- self._barsGraph.setMinimumSize(minimum_graph_size)
- self._barsGraph.setMaximumSize(maximum_graph_size)
- self._barsGraph.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- self._barsGraph.setFocusPolicy(Qt.StrongFocus)
- self._barsGraph.setResizeMode(QQuickWidget.SizeRootObjectToView)
- hLayout.addWidget(self._barsGraph, 1)
-
- vLayout = QVBoxLayout()
- hLayout.addLayout(vLayout)
-
- themeList = QComboBox(self._barsWidget)
- themeList.addItem("Qt")
- themeList.addItem("Primary Colors")
- themeList.addItem("Digia")
- themeList.addItem("Stone Moss")
- themeList.addItem("Army Blue")
- themeList.addItem("Retro")
- themeList.addItem("Ebony")
- themeList.addItem("Isabelle")
- themeList.setCurrentIndex(0)
-
- labelButton = QPushButton(self._barsWidget)
- labelButton.setText("Change label style")
-
- smoothCheckBox = QCheckBox(self._barsWidget)
- smoothCheckBox.setText("Smooth bars")
- smoothCheckBox.setChecked(False)
-
- barStyleList = QComboBox(self._barsWidget)
- barStyleList.addItem("Bar", QAbstract3DSeries.Mesh.Bar)
- barStyleList.addItem("Pyramid", QAbstract3DSeries.Mesh.Pyramid)
- barStyleList.addItem("Cone", QAbstract3DSeries.Mesh.Cone)
- barStyleList.addItem("Cylinder", QAbstract3DSeries.Mesh.Cylinder)
- barStyleList.addItem("Bevel bar", QAbstract3DSeries.Mesh.BevelBar)
- barStyleList.addItem("Sphere", QAbstract3DSeries.Mesh.Sphere)
- barStyleList.setCurrentIndex(4)
-
- cameraButton = QPushButton(self._barsWidget)
- cameraButton.setText("Change camera preset")
-
- zoomToSelectedButton = QPushButton(self._barsWidget)
- zoomToSelectedButton.setText("Zoom to selected bar")
-
- selectionModeList = QComboBox(self._barsWidget)
- selectionModeList.addItem("None", QAbstract3DGraph.SelectionNone)
- selectionModeList.addItem("Bar", QAbstract3DGraph.SelectionItem)
- selectionModeList.addItem("Row", QAbstract3DGraph.SelectionRow)
- sel = QAbstract3DGraph.SelectionItemAndRow
- selectionModeList.addItem("Bar and Row", sel)
- selectionModeList.addItem("Column", QAbstract3DGraph.SelectionColumn)
- sel = QAbstract3DGraph.SelectionItemAndColumn
- selectionModeList.addItem("Bar and Column", sel)
- sel = QAbstract3DGraph.SelectionRowAndColumn
- selectionModeList.addItem("Row and Column", sel)
- sel = QAbstract3DGraph.SelectionItemRowAndColumn
- selectionModeList.addItem("Bar, Row and Column", sel)
- sel = QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionRow
- selectionModeList.addItem("Slice into Row", sel)
- sel = QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionItemAndRow
- selectionModeList.addItem("Slice into Row and Item", sel)
- sel = QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionColumn
- selectionModeList.addItem("Slice into Column", sel)
- sel = (QAbstract3DGraph.SelectionSlice
- | QAbstract3DGraph.SelectionItemAndColumn)
- selectionModeList.addItem("Slice into Column and Item", sel)
- sel = (QAbstract3DGraph.SelectionItemRowAndColumn
- | QAbstract3DGraph.SelectionMultiSeries)
- selectionModeList.addItem("Multi: Bar, Row, Col", sel)
- sel = (QAbstract3DGraph.SelectionSlice
- | QAbstract3DGraph.SelectionItemAndRow
- | QAbstract3DGraph.SelectionMultiSeries)
- selectionModeList.addItem("Multi, Slice: Row, Item", sel)
- sel = (QAbstract3DGraph.SelectionSlice
- | QAbstract3DGraph.SelectionItemAndColumn
- | QAbstract3DGraph.SelectionMultiSeries)
- selectionModeList.addItem("Multi, Slice: Col, Item", sel)
- selectionModeList.setCurrentIndex(1)
-
- backgroundCheckBox = QCheckBox(self._barsWidget)
- backgroundCheckBox.setText("Show background")
- backgroundCheckBox.setChecked(False)
-
- gridCheckBox = QCheckBox(self._barsWidget)
- gridCheckBox.setText("Show grid")
- gridCheckBox.setChecked(True)
-
- seriesCheckBox = QCheckBox(self._barsWidget)
- seriesCheckBox.setText("Show second series")
- seriesCheckBox.setChecked(False)
-
- reverseValueAxisCheckBox = QCheckBox(self._barsWidget)
- reverseValueAxisCheckBox.setText("Reverse value axis")
- reverseValueAxisCheckBox.setChecked(False)
-
- reflectionCheckBox = QCheckBox(self._barsWidget)
- reflectionCheckBox.setText("Show reflections")
- reflectionCheckBox.setChecked(False)
-
- rotationSliderX = QSlider(Qt.Horizontal, self._barsWidget)
- rotationSliderX.setTickInterval(30)
- rotationSliderX.setTickPosition(QSlider.TicksBelow)
- rotationSliderX.setMinimum(-180)
- rotationSliderX.setValue(0)
- rotationSliderX.setMaximum(180)
- rotationSliderY = QSlider(Qt.Horizontal, self._barsWidget)
- rotationSliderY.setTickInterval(15)
- rotationSliderY.setTickPosition(QSlider.TicksAbove)
- rotationSliderY.setMinimum(-90)
- rotationSliderY.setValue(0)
- rotationSliderY.setMaximum(90)
-
- fontSizeSlider = QSlider(Qt.Horizontal, self._barsWidget)
- fontSizeSlider.setTickInterval(10)
- fontSizeSlider.setTickPosition(QSlider.TicksBelow)
- fontSizeSlider.setMinimum(1)
- fontSizeSlider.setValue(30)
- fontSizeSlider.setMaximum(100)
-
- fontList = QFontComboBox(self._barsWidget)
- fontList.setCurrentFont(QFont("Times New Roman"))
-
- shadowQuality = QComboBox(self._barsWidget)
- shadowQuality.addItem("None")
- shadowQuality.addItem("Low")
- shadowQuality.addItem("Medium")
- shadowQuality.addItem("High")
- shadowQuality.addItem("Low Soft")
- shadowQuality.addItem("Medium Soft")
- shadowQuality.addItem("High Soft")
- shadowQuality.setCurrentIndex(5)
-
- rangeList = QComboBox(self._barsWidget)
- rangeList.addItem("2015")
- rangeList.addItem("2016")
- rangeList.addItem("2017")
- rangeList.addItem("2018")
- rangeList.addItem("2019")
- rangeList.addItem("2020")
- rangeList.addItem("2021")
- rangeList.addItem("2022")
- rangeList.addItem("All")
- rangeList.setCurrentIndex(8)
-
- axisTitlesVisibleCB = QCheckBox(self._barsWidget)
- axisTitlesVisibleCB.setText("Axis titles visible")
- axisTitlesVisibleCB.setChecked(True)
-
- axisTitlesFixedCB = QCheckBox(self._barsWidget)
- axisTitlesFixedCB.setText("Axis titles fixed")
- axisTitlesFixedCB.setChecked(True)
-
- axisLabelRotationSlider = QSlider(Qt.Horizontal, self._barsWidget)
- axisLabelRotationSlider.setTickInterval(10)
- axisLabelRotationSlider.setTickPosition(QSlider.TicksBelow)
- axisLabelRotationSlider.setMinimum(0)
- axisLabelRotationSlider.setValue(30)
- axisLabelRotationSlider.setMaximum(90)
-
- modeGroup = QButtonGroup(self._barsWidget)
- modeWeather = QRadioButton("Temperature Data", self._barsWidget)
- modeWeather.setChecked(True)
- modeCustomProxy = QRadioButton("Custom Proxy Data", self._barsWidget)
- modeGroup.addButton(modeWeather)
- modeGroup.addButton(modeCustomProxy)
-
- vLayout.addWidget(QLabel("Rotate horizontally"))
- vLayout.addWidget(rotationSliderX, 0, Qt.AlignTop)
- vLayout.addWidget(QLabel("Rotate vertically"))
- vLayout.addWidget(rotationSliderY, 0, Qt.AlignTop)
- vLayout.addWidget(labelButton, 0, Qt.AlignTop)
- vLayout.addWidget(cameraButton, 0, Qt.AlignTop)
- vLayout.addWidget(zoomToSelectedButton, 0, Qt.AlignTop)
- vLayout.addWidget(backgroundCheckBox)
- vLayout.addWidget(gridCheckBox)
- vLayout.addWidget(smoothCheckBox)
- vLayout.addWidget(reflectionCheckBox)
- vLayout.addWidget(seriesCheckBox)
- vLayout.addWidget(reverseValueAxisCheckBox)
- vLayout.addWidget(axisTitlesVisibleCB)
- vLayout.addWidget(axisTitlesFixedCB)
- vLayout.addWidget(QLabel("Show year"))
- vLayout.addWidget(rangeList)
- vLayout.addWidget(QLabel("Change bar style"))
- vLayout.addWidget(barStyleList)
- vLayout.addWidget(QLabel("Change selection mode"))
- vLayout.addWidget(selectionModeList)
- vLayout.addWidget(QLabel("Change theme"))
- vLayout.addWidget(themeList)
- vLayout.addWidget(QLabel("Adjust shadow quality"))
- vLayout.addWidget(shadowQuality)
- vLayout.addWidget(QLabel("Change font"))
- vLayout.addWidget(fontList)
- vLayout.addWidget(QLabel("Adjust font size"))
- vLayout.addWidget(fontSizeSlider)
- vLayout.addWidget(QLabel("Axis label rotation"))
- vLayout.addWidget(axisLabelRotationSlider, 0, Qt.AlignTop)
- vLayout.addWidget(modeWeather, 0, Qt.AlignTop)
- vLayout.addWidget(modeCustomProxy, 1, Qt.AlignTop)
-
- self._modifier = GraphModifier(self._barsGraph, self)
-
- rotationSliderX.valueChanged.connect(self._modifier.rotateX)
- rotationSliderY.valueChanged.connect(self._modifier.rotateY)
-
- labelButton.clicked.connect(self._modifier.changeLabelBackground)
- cameraButton.clicked.connect(self._modifier.changePresetCamera)
- zoomToSelectedButton.clicked.connect(self._modifier.zoomToSelectedBar)
-
- backgroundCheckBox.stateChanged.connect(self._modifier.setBackgroundEnabled)
- gridCheckBox.stateChanged.connect(self._modifier.setGridEnabled)
- smoothCheckBox.stateChanged.connect(self._modifier.setSmoothBars)
- seriesCheckBox.stateChanged.connect(self._modifier.setSeriesVisibility)
- reverseValueAxisCheckBox.stateChanged.connect(self._modifier.setReverseValueAxis)
- reflectionCheckBox.stateChanged.connect(self._modifier.setReflection)
-
- self._modifier.backgroundEnabledChanged.connect(backgroundCheckBox.setChecked)
- self._modifier.gridEnabledChanged.connect(gridCheckBox.setChecked)
-
- rangeList.currentIndexChanged.connect(self._modifier.changeRange)
-
- barStyleList.currentIndexChanged.connect(self._modifier.changeStyle)
-
- selectionModeList.currentIndexChanged.connect(self._modifier.changeSelectionMode)
-
- themeList.currentIndexChanged.connect(self._modifier.changeTheme)
-
- shadowQuality.currentIndexChanged.connect(self._modifier.changeShadowQuality)
-
- self._modifier.shadowQualityChanged.connect(shadowQuality.setCurrentIndex)
- self._barsGraph.shadowQualityChanged.connect(self._modifier.shadowQualityUpdatedByVisual)
-
- fontSizeSlider.valueChanged.connect(self._modifier.changeFontSize)
- fontList.currentFontChanged.connect(self._modifier.changeFont)
-
- self._modifier.fontSizeChanged.connect(fontSizeSlider.setValue)
- self._modifier.fontChanged.connect(fontList.setCurrentFont)
-
- axisTitlesVisibleCB.stateChanged.connect(self._modifier.setAxisTitleVisibility)
- axisTitlesFixedCB.stateChanged.connect(self._modifier.setAxisTitleFixed)
- axisLabelRotationSlider.valueChanged.connect(self._modifier.changeLabelRotation)
-
- modeWeather.toggled.connect(self._modifier.setDataModeToWeather)
- modeCustomProxy.toggled.connect(self._modifier.setDataModeToCustom)
- modeWeather.toggled.connect(seriesCheckBox.setEnabled)
- modeWeather.toggled.connect(rangeList.setEnabled)
- modeWeather.toggled.connect(axisTitlesVisibleCB.setEnabled)
- modeWeather.toggled.connect(axisTitlesFixedCB.setEnabled)
- modeWeather.toggled.connect(axisLabelRotationSlider.setEnabled)
-
- def barsWidget(self):
- return self._barsWidget
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from enum import Enum
-from math import sin, cos, degrees
-
-from PySide6.QtCore import Qt
-from PySide6.QtGraphs import (QAbstract3DGraph, Q3DInputHandler)
-
-
-class InputState(Enum):
- StateNormal = 0
- StateDraggingX = 1
- StateDraggingZ = 2
- StateDraggingY = 3
-
-
-class CustomInputHandler(Q3DInputHandler):
-
- def __init__(self, graph, parent=None):
- super().__init__(parent)
- self._highlight = None
- self._mousePressed = False
- self._state = InputState.StateNormal
- self._axisX = None
- self._axisY = None
- self._axisZ = None
- self._speedModifier = 20.0
- self._aspectRatio = 0.0
- self._axisXMinValue = 0.0
- self._axisXMaxValue = 0.0
- self._axisXMinRange = 0.0
- self._axisZMinValue = 0.0
- self._axisZMaxValue = 0.0
- self._axisZMinRange = 0.0
- self._areaMinValue = 0.0
- self._areaMaxValue = 0.0
-
- # Connect to the item selection signal from graph
- graph.selectedElementChanged.connect(self.handleElementSelected)
-
- def setAspectRatio(self, ratio):
- self._aspectRatio = ratio
-
- def setHighlightSeries(self, series):
- self._highlight = series
-
- def setDragSpeedModifier(self, modifier):
- self._speedModifier = modifier
-
- def setLimits(self, min, max, minRange):
- self._areaMinValue = min
- self._areaMaxValue = max
- self._axisXMinValue = self._areaMinValue
- self._axisXMaxValue = self._areaMaxValue
- self._axisZMinValue = self._areaMinValue
- self._axisZMaxValue = self._areaMaxValue
- self._axisXMinRange = minRange
- self._axisZMinRange = minRange
-
- def setAxes(self, axisX, axisY, axisZ):
- self._axisX = axisX
- self._axisY = axisY
- self._axisZ = axisZ
-
- def mousePressEvent(self, event, mousePos):
- if Qt.LeftButton == event.button():
- self._highlight.setVisible(False)
- self._mousePressed = True
- super().mousePressEvent(event, mousePos)
-
- def wheelEvent(self, event):
- delta = float(event.angleDelta().y())
-
- self._axisXMinValue += delta
- self._axisXMaxValue -= delta
- self._axisZMinValue += delta
- self._axisZMaxValue -= delta
- self.checkConstraints()
-
- y = (self._axisXMaxValue - self._axisXMinValue) * self._aspectRatio
-
- self._axisX.setRange(self._axisXMinValue, self._axisXMaxValue)
- self._axisY.setRange(100.0, y)
- self._axisZ.setRange(self._axisZMinValue, self._axisZMaxValue)
-
- def mouseMoveEvent(self, event, mousePos):
- # Check if we're trying to drag axis label
- if self._mousePressed and self._state != InputState.StateNormal:
- self.setPreviousInputPos(self.inputPosition())
- self.setInputPosition(mousePos)
- self.handleAxisDragging()
- else:
- super().mouseMoveEvent(event, mousePos)
-
- def mouseReleaseEvent(self, event, mousePos):
- super().mouseReleaseEvent(event, mousePos)
- self._mousePressed = False
- self._state = InputState.StateNormal
-
- def handleElementSelected(self, type):
- if type == QAbstract3DGraph.ElementAxisXLabel:
- self._state = InputState.StateDraggingX
- elif type == QAbstract3DGraph.ElementAxisZLabel:
- self._state = InputState.StateDraggingZ
- else:
- self._state = InputState.StateNormal
-
- def handleAxisDragging(self):
- distance = 0.0
-
- # Get scene orientation from active camera
- xRotation = self.scene().cameraXRotation()
-
- # Calculate directional drag multipliers based on rotation
- xMulX = cos(degrees(xRotation))
- xMulY = sin(degrees(xRotation))
- zMulX = xMulY
- zMulY = xMulX
-
- # Get the drag amount
- move = self.inputPosition() - self.previousInputPos()
-
- # Adjust axes
- if self._state == InputState.StateDraggingX:
- distance = (move.x() * xMulX - move.y() * xMulY) * self._speedModifier
- self._axisXMinValue -= distance
- self._axisXMaxValue -= distance
- if self._axisXMinValue < self._areaMinValue:
- dist = self._axisXMaxValue - self._axisXMinValue
- self._axisXMinValue = self._areaMinValue
- self._axisXMaxValue = self._axisXMinValue + dist
-
- if self._axisXMaxValue > self._areaMaxValue:
- dist = self._axisXMaxValue - self._axisXMinValue
- self._axisXMaxValue = self._areaMaxValue
- self._axisXMinValue = self._axisXMaxValue - dist
-
- self._axisX.setRange(self._axisXMinValue, self._axisXMaxValue)
- elif self._state == InputState.StateDraggingZ:
- distance = (move.x() * zMulX + move.y() * zMulY) * self._speedModifier
- self._axisZMinValue += distance
- self._axisZMaxValue += distance
- if self._axisZMinValue < self._areaMinValue:
- dist = self._axisZMaxValue - self._axisZMinValue
- self._axisZMinValue = self._areaMinValue
- self._axisZMaxValue = self._axisZMinValue + dist
-
- if self._axisZMaxValue > self._areaMaxValue:
- dist = self._axisZMaxValue - self._axisZMinValue
- self._axisZMaxValue = self._areaMaxValue
- self._axisZMinValue = self._axisZMaxValue - dist
-
- self._axisZ.setRange(self._axisZMinValue, self._axisZMaxValue)
-
- def checkConstraints(self):
- if self._axisXMinValue < self._areaMinValue:
- self._axisXMinValue = self._areaMinValue
- if self._axisXMaxValue > self._areaMaxValue:
- self._axisXMaxValue = self._areaMaxValue
- # Don't allow too much zoom in
- range = self._axisXMaxValue - self._axisXMinValue
- if range < self._axisXMinRange:
- adjust = (self._axisXMinRange - range) / 2.0
- self._axisXMinValue -= adjust
- self._axisXMaxValue += adjust
-
- if self._axisZMinValue < self._areaMinValue:
- self._axisZMinValue = self._areaMinValue
- if self._axisZMaxValue > self._areaMaxValue:
- self._axisZMaxValue = self._areaMaxValue
- # Don't allow too much zoom in
- range = self._axisZMaxValue - self._axisZMinValue
- if range < self._axisZMinRange:
- adjust = (self._axisZMinRange - range) / 2.0
- self._axisZMinValue -= adjust
- self._axisZMaxValue += adjust
+++ /dev/null
-License information regarding the data obtained from National Land Survey of
-Finland http://www.maanmittauslaitos.fi/en
-- topographic model from Elevation model 2 m (U4421B, U4421D, U4422A and
- U4422C) 08/2014
-- map image extracted from Topographic map raster 1:50 000 (U442) 08/2014
-
-National Land Survey open data licence - version 1.0 - 1 May 2012
-
-1. General information
-
-The National Land Survey of Finland (hereinafter the Licensor), as the holder
-of the immaterial rights to the data, has granted on the terms mentioned below
-the right to use a copy (hereinafter data or dataset(s)) of the data (or a part
-of it).
-
-The Licensee is a natural or legal person who makes use of the data covered by
-this licence. The Licensee accepts the terms of this licence by receiving the
-dataset(s) covered by the licence.
-
-This Licence agreement does not create a co-operation or business relationship
-between the Licensee and the Licensor.
-
-2. Terms of the licence
-
-2.1. Right of use
-
-This licence grants a worldwide, free of charge and irrevocable parallel right
-of use to open data. According to the terms of the licence, data received by
-the Licensee can be freely:
- - copied, distributed and published,
- - modified and utilised commercially and non-commercially,
- - inserted into other products and
- - used as a part of a software application or service.
-
-2.2. Duties and responsibilities of the Licensee
-
-Through reasonable means suitable to the distribution medium or method which is
-used in conjunction with a product containing data or a service utilising data
-covered by this licence or while distributing data, the Licensee shall:
- - mention the name of the Licensor, the name of the dataset(s) and the time
- when the National Land Survey has delivered the dataset(s) (e.g.: contains
- data from the National Land Survey of Finland Topographic Database 06/2012)
- - provide a copy of this licence or a link to it, as well as
- - require third parties to provide the same information when granting rights
- to copies of dataset(s) or products and services containing such data and
- - remove the name of the Licensor from the product or service, if required to
- do so by the Licensor.
-
-The terms of this licence do not allow the Licensee to state in conjunction
-with the use of dataset(s) that the Licensor supports or recommends such use.
-
-2.3. Duties and responsibilities of the Licensor
-
-The Licensor shall ensure that
- - the Licensor has the right to grant rights to the dataset(s) in accordance
- with this licence.
-
-The data has been licensed "as is" and the Licensor
- - shall not be held responsible for any errors or omissions in the data,
- disclaims any warranty for the validity or up to date status of the data and
- shall be free from liability for direct or consequential damages arising
- from the use of data provided by the Licensor,
- - and is not obligated to ensure the continuous availability of the data, nor
- to announce in advance the interruption or cessation of availability, and
- the Licensor shall be free from liability for direct or consequential
- damages arising from any such interruption or cessation.
-
-3. Jurisdiction
-
-Finnish law shall apply to this licence.
-
-4. Changes to this licence
-
-The Licensor may at any time change the terms of the licence or apply a
-different licence to the data. The terms of this licence shall, however, still
-apply to such data that has been received prior to the change of the terms of
-the licence or the licence itself.
+++ /dev/null
-# Rainfall per month from 2010 to 2022 in Northern Finland (Oulu)
-# Format: year, month, rainfall
-2010,1, 0,
-2010,2, 3.4,
-2010,3, 52,
-2010,4, 33.8,
-2010,5, 45.6,
-2010,6, 43.8,
-2010,7, 104.6,
-2010,8, 105.4,
-2010,9, 107.2,
-2010,10,38.6,
-2010,11,17.8,
-2010,12,0,
-2011,1, 8.2,
-2011,2, 1.6,
-2011,3, 27.4,
-2011,4, 15.8,
-2011,5, 57.6,
-2011,6, 85.2,
-2011,7, 127,
-2011,8, 72.2,
-2011,9, 82.2,
-2011,10,62.4,
-2011,11,31.6,
-2011,12,53.8,
-2012,1, 0,
-2012,2, 5,
-2012,3, 32.4,
-2012,4, 57.6,
-2012,5, 71.4,
-2012,6, 60.8,
-2012,7, 109,
-2012,8, 43.6,
-2012,9, 79.4,
-2012,10,117.2,
-2012,11,59,
-2012,12,0.2,
-2013,1, 28,
-2013,2, 19,
-2013,3, 0,
-2013,4, 37.6,
-2013,5, 44.2,
-2013,6, 104.8,
-2013,7, 84.2,
-2013,8, 57.2,
-2013,9, 37.2,
-2013,10,64.6,
-2013,11,77.8,
-2013,12,92.8,
-2014,1, 23.8,
-2014,2, 23.6,
-2014,3, 15.4,
-2014,4, 13.2,
-2014,5, 36.4,
-2014,6, 26.4,
-2014,7, 95.8,
-2014,8, 81.8,
-2014,9, 13.8,
-2014,10,94.6,
-2014,11,44.6,
-2014,12,31,
-2015,1, 37.4,
-2015,2, 21,
-2015,3, 42,
-2015,4, 8.8,
-2015,5, 82.4,
-2015,6, 150,
-2015,7, 56.8,
-2015,8, 67.2,
-2015,9, 131.2,
-2015,10,38.4,
-2015,11,83.4,
-2015,12,47.8,
-2016,1, 12.4,
-2016,2, 34.8,
-2016,3, 29,
-2016,4, 40.4,
-2016,5, 32.4,
-2016,6, 80.2,
-2016,7, 102.6,
-2016,8, 95.6,
-2016,9, 40.2,
-2016,10,7.8,
-2016,11,39.6,
-2016,12,8.8,
-2017,1, 9.4,
-2017,2, 6.6,
-2017,3, 29,
-2017,4, 46.2,
-2017,5, 43.2,
-2017,6, 25.2,
-2017,7, 72.4,
-2017,8, 58.8,
-2017,9, 68.8,
-2017,10,45.8,
-2017,11,36.8,
-2017,12,29.6,
-2018,1, 19.8,
-2018,2, 0.8,
-2018,3, 4,
-2018,4, 23.2,
-2018,5, 13.2,
-2018,6, 62.8,
-2018,7, 33,
-2018,8, 96.6,
-2018,9, 72.6,
-2018,10,48.8,
-2018,11,31.8,
-2018,12,12.8,
-2019,1, 0.2,
-2019,2, 24.8,
-2019,3, 32,
-2019,4, 8.8,
-2019,5, 71.4,
-2019,6, 65.8,
-2019,7, 17.6,
-2019,8, 90,
-2019,9, 50,
-2019,10,77,
-2019,11,27,
-2019,12,43.2,
-2020,1, 28.8,
-2020,2, 45,
-2020,3, 18.6,
-2020,4, 13,
-2020,5, 30.8,
-2020,6, 21.4,
-2020,7, 163.6,
-2020,8, 12,
-2020,9, 102.4,
-2020,10,133.2,
-2020,11,69.8,
-2020,12,40.6,
-2021,1, 0.4,
-2021,2, 21.6,
-2021,3, 24,
-2021,4, 51.4,
-2021,5, 76.4,
-2021,6, 29.2,
-2021,7, 36.4,
-2021,8, 116,
-2021,9, 72.4,
-2021,10,93.4,
-2021,11,21,
-2021,12,10.2,
-2022,1, 8.6,
-2022,2, 6.6,
-2022,3, 5.2,
-2022,4, 15.2,
-2022,5, 37.6,
-2022,6, 45,
-2022,7, 67.4,
-2022,8, 161.6,
-2022,9, 22.8,
-2022,10,75.2,
-2022,11,21.8,
-2022,12,0.2
+++ /dev/null
-Widget Gallery
-==============
-
-
-Widget Gallery demonstrates all three graph types and some of their special
-features. The graphs have their own tabs in the application.
-
-
-.. image:: widgetgallery.webp
- :width: 400
- :alt: Widget Screenshot
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-
-from math import atan, degrees
-import numpy as np
-
-from PySide6.QtCore import QObject, QPropertyAnimation, Signal, Slot
-from PySide6.QtGui import QFont, QVector3D
-from PySide6.QtGraphs import (QAbstract3DGraph, QAbstract3DSeries,
- QBarDataItem, QBar3DSeries, QCategory3DAxis,
- QValue3DAxis, Q3DTheme)
-
-from rainfalldata import RainfallData
-
-# Set up data
-TEMP_OULU = np.array([
- [-7.4, -2.4, 0.0, 3.0, 8.2, 11.6, 14.7, 15.4, 11.4, 4.2, 2.1, -2.3], # 2015
- [-13.4, -3.9, -1.8, 3.1, 10.6, 13.7, 17.8, 13.6, 10.7, 3.5, -3.1, -4.2], # 2016
- [-5.7, -6.7, -3.0, -0.1, 4.7, 12.4, 16.1, 14.1, 9.4, 3.0, -0.3, -3.2], # 2017
- [-6.4, -11.9, -7.4, 1.9, 11.4, 12.4, 21.5, 16.1, 11.0, 4.4, 2.1, -4.1], # 2018
- [-11.7, -6.1, -2.4, 3.9, 7.2, 14.5, 15.6, 14.4, 8.5, 2.0, -3.0, -1.5], # 2019
- [-2.1, -3.4, -1.8, 0.6, 7.0, 17.1, 15.6, 15.4, 11.1, 5.6, 1.9, -1.7], # 2020
- [-9.6, -11.6, -3.2, 2.4, 7.8, 17.3, 19.4, 14.2, 8.0, 5.2, -2.2, -8.6], # 2021
- [-7.3, -6.4, -1.8, 1.3, 8.1, 15.5, 17.6, 17.6, 9.1, 5.4, -1.5, -4.4]], # 2022
- np.float64)
-
-
-TEMP_HELSINKI = np.array([
- [-2.0, -0.1, 1.8, 5.1, 9.7, 13.7, 16.3, 17.3, 12.7, 5.4, 4.6, 2.1], # 2015
- [-10.3, -0.6, 0.0, 4.9, 14.3, 15.7, 17.7, 16.0, 12.7, 4.6, -1.0, -0.9], # 2016
- [-2.9, -3.3, 0.7, 2.3, 9.9, 13.8, 16.1, 15.9, 11.4, 5.0, 2.7, 0.7], # 2017
- [-2.2, -8.4, -4.7, 5.0, 15.3, 15.8, 21.2, 18.2, 13.3, 6.7, 2.8, -2.0], # 2018
- [-6.2, -0.5, -0.3, 6.8, 10.6, 17.9, 17.5, 16.8, 11.3, 5.2, 1.8, 1.4], # 2019
- [1.9, 0.5, 1.7, 4.5, 9.5, 18.4, 16.5, 16.8, 13.0, 8.2, 4.4, 0.9], # 2020
- [-4.7, -8.1, -0.9, 4.5, 10.4, 19.2, 20.9, 15.4, 9.5, 8.0, 1.5, -6.7], # 2021
- [-3.3, -2.2, -0.2, 3.3, 9.6, 16.9, 18.1, 18.9, 9.2, 7.6, 2.3, -3.4]], # 2022
- np.float64)
-
-
-class GraphModifier(QObject):
-
- shadowQualityChanged = Signal(int)
- backgroundEnabledChanged = Signal(bool)
- gridEnabledChanged = Signal(bool)
- fontChanged = Signal(QFont)
- fontSizeChanged = Signal(int)
-
- def __init__(self, bargraph, parent):
- super().__init__(parent)
- self._graph = bargraph
- self._temperatureAxis = QValue3DAxis()
- self._yearAxis = QCategory3DAxis()
- self._monthAxis = QCategory3DAxis()
- self._primarySeries = QBar3DSeries()
- self._secondarySeries = QBar3DSeries()
- self._celsiusString = "°C"
-
- self._xRotation = float(0)
- self._yRotation = float(0)
- self._fontSize = 30
- self._segments = 4
- self._subSegments = 3
- self._minval = float(-20)
- self._maxval = float(20)
- self._barMesh = QAbstract3DSeries.Mesh.BevelBar
- self._smooth = False
- self._animationCameraX = QPropertyAnimation()
- self._animationCameraY = QPropertyAnimation()
- self._animationCameraZoom = QPropertyAnimation()
- self._animationCameraTarget = QPropertyAnimation()
- self._defaultAngleX = float(0)
- self._defaultAngleY = float(0)
- self._defaultZoom = float(0)
- self._defaultTarget = []
- self._customData = None
-
- self._graph.setShadowQuality(QAbstract3DGraph.ShadowQuality.SoftMedium)
- theme = self._graph.activeTheme()
- theme.setBackgroundEnabled(False)
- theme.setFont(QFont("Times New Roman", self._fontSize))
- theme.setLabelBackgroundEnabled(True)
- self._graph.setMultiSeriesUniform(True)
-
- self._months = ["January", "February", "March", "April", "May", "June",
- "July", "August", "September", "October", "November",
- "December"]
- self._years = ["2015", "2016", "2017", "2018", "2019", "2020",
- "2021", "2022"]
-
- self._temperatureAxis.setTitle("Average temperature")
- self._temperatureAxis.setSegmentCount(self._segments)
- self._temperatureAxis.setSubSegmentCount(self._subSegments)
- self._temperatureAxis.setRange(self._minval, self._maxval)
- self._temperatureAxis.setLabelFormat("%.1f " + self._celsiusString)
- self._temperatureAxis.setLabelAutoRotation(30.0)
- self._temperatureAxis.setTitleVisible(True)
-
- self._yearAxis.setTitle("Year")
- self._yearAxis.setLabelAutoRotation(30.0)
- self._yearAxis.setTitleVisible(True)
- self._monthAxis.setTitle("Month")
- self._monthAxis.setLabelAutoRotation(30.0)
- self._monthAxis.setTitleVisible(True)
-
- self._graph.setValueAxis(self._temperatureAxis)
- self._graph.setRowAxis(self._yearAxis)
- self._graph.setColumnAxis(self._monthAxis)
-
- format = "Oulu - @colLabel @rowLabel: @valueLabel"
- self._primarySeries.setItemLabelFormat(format)
- self._primarySeries.setMesh(QAbstract3DSeries.Mesh.BevelBar)
- self._primarySeries.setMeshSmooth(False)
-
- format = "Helsinki - @colLabel @rowLabel: @valueLabel"
- self._secondarySeries.setItemLabelFormat(format)
- self._secondarySeries.setMesh(QAbstract3DSeries.Mesh.BevelBar)
- self._secondarySeries.setMeshSmooth(False)
- self._secondarySeries.setVisible(False)
-
- self._graph.addSeries(self._primarySeries)
- self._graph.addSeries(self._secondarySeries)
-
- self.changePresetCamera()
-
- self.resetTemperatureData()
-
- # Set up property animations for zooming to the selected bar
- self._defaultAngleX = self._graph.cameraXRotation()
- self._defaultAngleY = self._graph.cameraYRotation()
- self._defaultZoom = self._graph.cameraZoomLevel()
- self._defaultTarget = self._graph.cameraTargetPosition()
-
- self._animationCameraX.setTargetObject(self._graph)
- self._animationCameraY.setTargetObject(self._graph)
- self._animationCameraZoom.setTargetObject(self._graph)
- self._animationCameraTarget.setTargetObject(self._graph)
-
- self._animationCameraX.setPropertyName(b"cameraXRotation")
- self._animationCameraY.setPropertyName(b"cameraYRotation")
- self._animationCameraZoom.setPropertyName(b"cameraZoomLevel")
- self._animationCameraTarget.setPropertyName(b"cameraTargetPosition")
-
- duration = 1700
- self._animationCameraX.setDuration(duration)
- self._animationCameraY.setDuration(duration)
- self._animationCameraZoom.setDuration(duration)
- self._animationCameraTarget.setDuration(duration)
-
- # The zoom always first zooms out above the graph and then zooms in
- zoomOutFraction = 0.3
- self._animationCameraX.setKeyValueAt(zoomOutFraction, 0.0)
- self._animationCameraY.setKeyValueAt(zoomOutFraction, 90.0)
- self._animationCameraZoom.setKeyValueAt(zoomOutFraction, 50.0)
- self._animationCameraTarget.setKeyValueAt(zoomOutFraction,
- QVector3D(0, 0, 0))
- self._customData = RainfallData()
-
- def resetTemperatureData(self):
- # Create data arrays
- dataSet = []
- dataSet2 = []
-
- for year in range(0, len(self._years)):
- # Create a data row
- dataRow = []
- dataRow2 = []
- for month in range(0, len(self._months)):
- # Add data to the row
- item = QBarDataItem()
- item.setValue(TEMP_OULU[year][month])
- dataRow.append(item)
- item = QBarDataItem()
- item.setValue(TEMP_HELSINKI[year][month])
- dataRow2.append(item)
-
- # Add the row to the set
- dataSet.append(dataRow)
- dataSet2.append(dataRow2)
-
- # Add data to the data proxy (the data proxy assumes ownership of it)
- self._primarySeries.dataProxy().resetArray(dataSet, self._years, self._months)
- self._secondarySeries.dataProxy().resetArray(dataSet2, self._years, self._months)
-
- @Slot(int)
- def changeRange(self, range):
- if range >= len(self._years):
- self._yearAxis.setRange(0, len(self._years) - 1)
- else:
- self._yearAxis.setRange(range, range)
-
- @Slot(int)
- def changeStyle(self, style):
- comboBox = self.sender()
- if comboBox:
- self._barMesh = comboBox.itemData(style)
- self._primarySeries.setMesh(self._barMesh)
- self._secondarySeries.setMesh(self._barMesh)
- self._customData.customSeries().setMesh(self._barMesh)
-
- def changePresetCamera(self):
- self._animationCameraX.stop()
- self._animationCameraY.stop()
- self._animationCameraZoom.stop()
- self._animationCameraTarget.stop()
-
- # Restore camera target in case animation has changed it
- self._graph.setCameraTargetPosition(QVector3D(0.0, 0.0, 0.0))
-
- self._preset = QAbstract3DGraph.CameraPreset.Front.value
-
- self._graph.setCameraPreset(QAbstract3DGraph.CameraPreset(self._preset))
-
- self._preset += 1
- if self._preset > QAbstract3DGraph.CameraPreset.DirectlyBelow.value:
- self._preset = QAbstract3DGraph.CameraPreset.FrontLow.value
-
- @Slot(int)
- def changeTheme(self, theme):
- currentTheme = self._graph.activeTheme()
- currentTheme.setType(Q3DTheme.Theme(theme))
- self.backgroundEnabledChanged.emit(currentTheme.isBackgroundEnabled())
- self.gridEnabledChanged.emit(currentTheme.isGridEnabled())
- self.fontChanged.emit(currentTheme.font())
- self.fontSizeChanged.emit(currentTheme.font().pointSize())
-
- def changeLabelBackground(self):
- theme = self._graph.activeTheme()
- theme.setLabelBackgroundEnabled(not theme.isLabelBackgroundEnabled())
-
- @Slot(int)
- def changeSelectionMode(self, selectionMode):
- comboBox = self.sender()
- if comboBox:
- flags = comboBox.itemData(selectionMode)
- self._graph.setSelectionMode(QAbstract3DGraph.SelectionFlags(flags))
-
- def changeFont(self, font):
- newFont = font
- self._graph.activeTheme().setFont(newFont)
-
- def changeFontSize(self, fontsize):
- self._fontSize = fontsize
- font = self._graph.activeTheme().font()
- font.setPointSize(self._fontSize)
- self._graph.activeTheme().setFont(font)
-
- @Slot(QAbstract3DGraph.ShadowQuality)
- def shadowQualityUpdatedByVisual(self, sq):
- # Updates the UI component to show correct shadow quality
- self.shadowQualityChanged.emit(sq.value)
-
- @Slot(int)
- def changeLabelRotation(self, rotation):
- self._temperatureAxis.setLabelAutoRotation(float(rotation))
- self._monthAxis.setLabelAutoRotation(float(rotation))
- self._yearAxis.setLabelAutoRotation(float(rotation))
-
- @Slot(bool)
- def setAxisTitleVisibility(self, enabled):
- self._temperatureAxis.setTitleVisible(enabled)
- self._monthAxis.setTitleVisible(enabled)
- self._yearAxis.setTitleVisible(enabled)
-
- @Slot(bool)
- def setAxisTitleFixed(self, enabled):
- self._temperatureAxis.setTitleFixed(enabled)
- self._monthAxis.setTitleFixed(enabled)
- self._yearAxis.setTitleFixed(enabled)
-
- @Slot()
- def zoomToSelectedBar(self):
- self._animationCameraX.stop()
- self._animationCameraY.stop()
- self._animationCameraZoom.stop()
- self._animationCameraTarget.stop()
-
- currentX = self._graph.cameraXRotation()
- currentY = self._graph.cameraYRotation()
- currentZoom = self._graph.cameraZoomLevel()
- currentTarget = self._graph.cameraTargetPosition()
-
- self._animationCameraX.setStartValue(currentX)
- self._animationCameraY.setStartValue(currentY)
- self._animationCameraZoom.setStartValue(currentZoom)
- self._animationCameraTarget.setStartValue(currentTarget)
-
- selectedBar = (self._graph.selectedSeries().selectedBar()
- if self._graph.selectedSeries()
- else QBar3DSeries.invalidSelectionPosition())
-
- if selectedBar != QBar3DSeries.invalidSelectionPosition():
- # Normalize selected bar position within axis range to determine
- # target coordinates
- endTarget = QVector3D()
- xMin = self._graph.columnAxis().min()
- xRange = self._graph.columnAxis().max() - xMin
- zMin = self._graph.rowAxis().min()
- zRange = self._graph.rowAxis().max() - zMin
- endTarget.setX((selectedBar.y() - xMin) / xRange * 2.0 - 1.0)
- endTarget.setZ((selectedBar.x() - zMin) / zRange * 2.0 - 1.0)
-
- # Rotate the camera so that it always points approximately to the
- # graph center
- endAngleX = 90.0 - degrees(atan(float(endTarget.z() / endTarget.x())))
- if endTarget.x() > 0.0:
- endAngleX -= 180.0
- proxy = self._graph.selectedSeries().dataProxy()
- barValue = proxy.itemAt(selectedBar.x(), selectedBar.y()).value()
- endAngleY = 30.0 if barValue >= 0.0 else -30.0
- if self._graph.valueAxis().reversed():
- endAngleY *= -1.0
-
- self._animationCameraX.setEndValue(float(endAngleX))
- self._animationCameraY.setEndValue(endAngleY)
- self._animationCameraZoom.setEndValue(250)
- self._animationCameraTarget.setEndValue(endTarget)
- else:
- # No selected bar, so return to the default view
- self._animationCameraX.setEndValue(self._defaultAngleX)
- self._animationCameraY.setEndValue(self._defaultAngleY)
- self._animationCameraZoom.setEndValue(self._defaultZoom)
- self._animationCameraTarget.setEndValue(self._defaultTarget)
-
- self._animationCameraX.start()
- self._animationCameraY.start()
- self._animationCameraZoom.start()
- self._animationCameraTarget.start()
-
- @Slot(bool)
- def setDataModeToWeather(self, enabled):
- if enabled:
- self.changeDataMode(False)
-
- @Slot(bool)
- def setDataModeToCustom(self, enabled):
- if enabled:
- self.changeDataMode(True)
-
- def changeShadowQuality(self, quality):
- sq = QAbstract3DGraph.ShadowQuality(quality)
- self._graph.setShadowQuality(sq)
- self.shadowQualityChanged.emit(quality)
-
- def rotateX(self, rotation):
- self._xRotation = rotation
- camera = self._graph.scene().activeCamera()
- camera.setCameraPosition(self._xRotation, self._yRotation)
-
- def rotateY(self, rotation):
- self._yRotation = rotation
- camera = self._graph.scene().activeCamera()
- camera.setCameraPosition(self._xRotation, self._yRotation)
-
- def setBackgroundEnabled(self, enabled):
- self._graph.activeTheme().setBackgroundEnabled(bool(enabled))
-
- def setGridEnabled(self, enabled):
- self._graph.activeTheme().setGridEnabled(bool(enabled))
-
- def setSmoothBars(self, smooth):
- self._smooth = bool(smooth)
- self._primarySeries.setMeshSmooth(self._smooth)
- self._secondarySeries.setMeshSmooth(self._smooth)
- self._customData.customSeries().setMeshSmooth(self._smooth)
-
- def setSeriesVisibility(self, enabled):
- self._secondarySeries.setVisible(bool(enabled))
-
- def setReverseValueAxis(self, enabled):
- self._graph.valueAxis().setReversed(enabled)
-
- def setReflection(self, enabled):
- self._graph.setReflection(enabled)
-
- def changeDataMode(self, customData):
- # Change between weather data and data from custom proxy
- if customData:
- self._graph.removeSeries(self._primarySeries)
- self._graph.removeSeries(self._secondarySeries)
- self._graph.addSeries(self._customData.customSeries())
- self._graph.setValueAxis(self._customData.valueAxis())
- self._graph.setRowAxis(self._customData.rowAxis())
- self._graph.setColumnAxis(self._customData.colAxis())
- else:
- self._graph.removeSeries(self._customData.customSeries())
- self._graph.addSeries(self._primarySeries)
- self._graph.addSeries(self._secondarySeries)
- self._graph.setValueAxis(self._temperatureAxis)
- self._graph.setRowAxis(self._yearAxis)
- self._graph.setColumnAxis(self._monthAxis)
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import QPoint, Qt, Slot
-from PySide6.QtGui import QLinearGradient, QVector3D
-from PySide6.QtGraphs import (QSurface3DSeries, QSurfaceDataItem, Q3DTheme)
-
-
-DARK_RED_POS = 1.0
-RED_POS = 0.8
-YELLOW_POS = 0.6
-GREEN_POS = 0.4
-DARK_GREEN_POS = 0.2
-
-
-class HighlightSeries(QSurface3DSeries):
-
- def __init__(self):
- super().__init__()
- self._width = 100
- self._height = 100
- self._srcWidth = 0
- self._srcHeight = 0
- self._position = {}
- self._topographicSeries = None
- self._minHeight = 0.0
- self.setDrawMode(QSurface3DSeries.DrawSurface)
- self.setFlatShadingEnabled(True)
- self.setVisible(False)
-
- def setTopographicSeries(self, series):
- self._topographicSeries = series
- array = self._topographicSeries.dataProxy().array()
- self._srcWidth = len(array[0])
- self._srcHeight = len(array)
- self._topographicSeries.selectedPointChanged.connect(self.handlePositionChange)
-
- def setMinHeight(self, height):
- self. m_minHeight = height
-
- @Slot(QPoint)
- def handlePositionChange(self, position):
- self._position = position
-
- if position == self.invalidSelectionPosition():
- self.setVisible(False)
- return
-
- halfWidth = self._width / 2
- halfHeight = self._height / 2
-
- startX = position.y() - halfWidth
- if startX < 0:
- startX = 0
- endX = position.y() + halfWidth
- if endX > (self._srcWidth - 1):
- endX = self._srcWidth - 1
- startZ = position.x() - halfHeight
- if startZ < 0:
- startZ = 0
- endZ = position.x() + halfHeight
- if endZ > (self._srcHeight - 1):
- endZ = self._srcHeight - 1
-
- srcProxy = self._topographicSeries.dataProxy()
- srcArray = srcProxy.array()
-
- dataArray = []
- for i in range(int(startZ), int(endZ)):
- newRow = []
- srcRow = srcArray[i]
- for j in range(startX, endX):
- pos = srcRow.at(j).position()
- pos.setY(pos.y() + 0.1)
- item = QSurfaceDataItem(QVector3D(pos))
- newRow.append(item)
- dataArray.append(newRow)
- self.dataProxy().resetArray(dataArray)
- self.setVisible(True)
-
- @Slot(float)
- def handleGradientChange(self, value):
- ratio = self._minHeight / value
-
- gr = QLinearGradient()
- gr.setColorAt(0.0, Qt.black)
- gr.setColorAt(DARK_GREEN_POS * ratio, Qt.darkGreen)
- gr.setColorAt(GREEN_POS * ratio, Qt.green)
- gr.setColorAt(YELLOW_POS * ratio, Qt.yellow)
- gr.setColorAt(RED_POS * ratio, Qt.red)
- gr.setColorAt(DARK_RED_POS * ratio, Qt.darkRed)
-
- self.setBaseGradient(gr)
- self.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-"""PySide6 port of the Qt Graphs widgetgallery example from Qt v6.x"""
-
-import sys
-
-from PySide6.QtCore import QSize
-from PySide6.QtWidgets import QApplication, QTabWidget
-
-from bargraph import BarGraph
-from scattergraph import ScatterGraph
-from surfacegraph import SurfaceGraph
-
-
-if __name__ == "__main__":
- app = QApplication(sys.argv)
-
- # Create a tab widget for creating own tabs for Q3DBars, Q3DScatter, and Q3DSurface
- tabWidget = QTabWidget()
- tabWidget.setWindowTitle("Widget Gallery")
-
- screen_size = tabWidget.screen().size()
- minimum_graph_size = QSize(screen_size.width() / 2, screen_size.height() / 1.75)
-
- # Create bar graph
- bars = BarGraph(minimum_graph_size, screen_size)
- # Create scatter graph
- scatter = ScatterGraph(minimum_graph_size, screen_size)
- # Create surface graph
- surface = SurfaceGraph(minimum_graph_size, screen_size)
-
- # Add bars widget
- tabWidget.addTab(bars.barsWidget(), "Bar Graph")
- # Add scatter widget
- tabWidget.addTab(scatter.scatterWidget(), "Scatter Graph")
- # Add surface widget
- tabWidget.addTab(surface.surfaceWidget(), "Surface Graph")
-
- tabWidget.show()
- sys.exit(app.exec())
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import sys
-
-from pathlib import Path
-
-from PySide6.QtCore import QFile, QIODevice, QObject
-from PySide6.QtGraphs import (QBar3DSeries, QCategory3DAxis, QValue3DAxis)
-
-from variantbardataproxy import VariantBarDataProxy
-from variantbardatamapping import VariantBarDataMapping
-from variantdataset import VariantDataSet
-
-
-MONTHS = ["January", "February", "March", "April",
- "May", "June", "July", "August", "September", "October",
- "November", "December"]
-
-
-class RainfallData(QObject):
-
- def __init__(self):
- super().__init__()
- self._columnCount = 0
- self._rowCount = 0
- self._years = []
- self._numericMonths = []
- self._proxy = VariantBarDataProxy()
- self._mapping = None
- self._dataSet = None
- self._series = QBar3DSeries()
- self._valueAxis = QValue3DAxis()
- self._rowAxis = QCategory3DAxis()
- self._colAxis = QCategory3DAxis()
-
- # In data file the months are in numeric format, so create custom list
- for i in range(1, 13):
- self._numericMonths.append(str(i))
-
- self._columnCount = len(self._numericMonths)
-
- self.updateYearsList(2010, 2022)
-
- # Create proxy and series
- self._proxy = VariantBarDataProxy()
- self._series = QBar3DSeries(self._proxy)
-
- self._series.setItemLabelFormat("%.1f mm")
-
- # Create the axes
- self._rowAxis = QCategory3DAxis(self)
- self._colAxis = QCategory3DAxis(self)
- self._valueAxis = QValue3DAxis(self)
- self._rowAxis.setAutoAdjustRange(True)
- self._colAxis.setAutoAdjustRange(True)
- self._valueAxis.setAutoAdjustRange(True)
-
- # Set axis labels and titles
- self._rowAxis.setTitle("Year")
- self._colAxis.setTitle("Month")
- self._valueAxis.setTitle("rainfall (mm)")
- self._valueAxis.setSegmentCount(5)
- self._rowAxis.setLabels(self._years)
- self._colAxis.setLabels(MONTHS)
- self._rowAxis.setTitleVisible(True)
- self._colAxis.setTitleVisible(True)
- self._valueAxis.setTitleVisible(True)
-
- self.addDataSet()
-
- def customSeries(self):
- return self._series
-
- def valueAxis(self):
- return self._valueAxis
-
- def rowAxis(self):
- return self._rowAxis
-
- def colAxis(self):
- return self._colAxis
-
- def updateYearsList(self, start, end):
- self._years.clear()
- for i in range(start, end + 1):
- self._years.append(str(i))
- self._rowCount = len(self._years)
-
- def addDataSet(self):
- # Create a new variant data set and data item list
- self._dataSet = VariantDataSet()
- itemList = []
-
- # Read data from a data file into the data item list
- file_path = Path(__file__).resolve().parent / "data" / "raindata.txt"
- dataFile = QFile(file_path)
- if dataFile.open(QIODevice.ReadOnly | QIODevice.Text):
- data = dataFile.readAll().data().decode("utf8")
- for line in data.split("\n"):
- if line and not line.startswith("#"): # Ignore comments
- tokens = line.split(",")
- # Each line has three data items: Year, month, and
- # rainfall value
- if len(tokens) >= 3:
- # Store year and month as strings, and rainfall value
- # as double into a variant data item and add the item to
- # the item list.
- newItem = []
- newItem.append(tokens[0].strip())
- newItem.append(tokens[1].strip())
- newItem.append(float(tokens[2].strip()))
- itemList.append(newItem)
- else:
- print("Unable to open data file:", dataFile.fileName(),
- file=sys.stderr)
-
- # Add items to the data set and set it to the proxy
- self._dataSet.addItems(itemList)
- self._proxy.setDataSet(self._dataSet)
-
- # Create new mapping for the data and set it to the proxy
- self._mapping = VariantBarDataMapping(0, 1, 2,
- self._years, self._numericMonths)
- self._proxy.setMapping(self._mapping)
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from math import cos, degrees, sqrt
-
-from PySide6.QtCore import QObject, Signal, Slot, Qt
-from PySide6.QtGui import QVector3D
-from PySide6.QtGraphs import (QAbstract3DGraph, QAbstract3DSeries,
- QScatterDataItem, QScatterDataProxy,
- QScatter3DSeries, Q3DTheme)
-
-from axesinputhandler import AxesInputHandler
-
-
-NUMBER_OF_ITEMS = 10000
-CURVE_DIVIDER = 7.5
-LOWER_NUMBER_OF_ITEMS = 900
-LOWER_CURVE_DIVIDER = 0.75
-
-
-class ScatterDataModifier(QObject):
-
- backgroundEnabledChanged = Signal(bool)
- gridEnabledChanged = Signal(bool)
- shadowQualityChanged = Signal(int)
-
- def __init__(self, scatter, parent):
- super().__init__(parent)
-
- self._graph = scatter
-
- self._style = QAbstract3DSeries.Mesh.Sphere
- self._smooth = True
- self._inputHandler = AxesInputHandler(scatter)
- self._autoAdjust = True
- self._itemCount = LOWER_NUMBER_OF_ITEMS
- self._CURVE_DIVIDER = LOWER_CURVE_DIVIDER
- self._inputHandler = AxesInputHandler(scatter)
-
- self._graph.activeTheme().setType(Q3DTheme.Theme.StoneMoss)
- self._graph.setShadowQuality(QAbstract3DGraph.ShadowQuality.SoftHigh)
- self._graph.setCameraPreset(QAbstract3DGraph.CameraPreset.Front)
- self._graph.setCameraZoomLevel(80.0)
-
- self._proxy = QScatterDataProxy()
- self._series = QScatter3DSeries(self._proxy)
- self._series.setItemLabelFormat("@xTitle: @xLabel @yTitle: @yLabel @zTitle: @zLabel")
- self._series.setMeshSmooth(self._smooth)
- self._graph.addSeries(self._series)
- self._preset = QAbstract3DGraph.CameraPreset.FrontLow.value
-
- # Give ownership of the handler to the graph and make it the active
- # handler
- self._graph.setActiveInputHandler(self._inputHandler)
-
- # Give our axes to the input handler
- self._inputHandler.setAxes(self._graph.axisX(), self._graph.axisZ(),
- self._graph.axisY())
-
- self.addData()
-
- def addData(self):
- # Configure the axes according to the data
- self._graph.axisX().setTitle("X")
- self._graph.axisY().setTitle("Y")
- self._graph.axisZ().setTitle("Z")
-
- dataArray = []
- limit = int(sqrt(self._itemCount) / 2.0)
- for i in range(-limit, limit):
- for j in range(-limit, limit):
- x = float(i) + 0.5
- y = cos(degrees(float(i * j) / self._CURVE_DIVIDER))
- z = float(j) + 0.5
- dataArray.append(QScatterDataItem(QVector3D(x, y, z)))
-
- self._graph.seriesList()[0].dataProxy().resetArray(dataArray)
-
- @Slot(int)
- def changeStyle(self, style):
- comboBox = self.sender()
- if comboBox:
- self._style = comboBox.itemData(style)
- if self._graph.seriesList():
- self._graph.seriesList()[0].setMesh(self._style)
-
- @Slot(int)
- def setSmoothDots(self, smooth):
- self._smooth = smooth == Qt.Checked.value
- series = self._graph.seriesList()[0]
- series.setMeshSmooth(self._smooth)
-
- @Slot(int)
- def changeTheme(self, theme):
- currentTheme = self._graph.activeTheme()
- currentTheme.setType(Q3DTheme.Theme(theme))
- self.backgroundEnabledChanged.emit(currentTheme.isBackgroundEnabled())
- self.gridEnabledChanged.emit(currentTheme.isGridEnabled())
-
- @Slot()
- def changePresetCamera(self):
- camera = self._graph.scene().activeCamera()
- camera.setCameraPreset(QAbstract3DGraph.CameraPreset(self._preset))
-
- self._preset += 1
- if self._preset > QAbstract3DGraph.CameraPreset.DirectlyBelow.value:
- self._preset = QAbstract3DGraph.CameraPreset.FrontLow.value
-
- @Slot(QAbstract3DGraph.ShadowQuality)
- def shadowQualityUpdatedByVisual(self, sq):
- self.shadowQualityChanged.emit(sq.value)
-
- @Slot(int)
- def changeShadowQuality(self, quality):
- sq = QAbstract3DGraph.ShadowQuality(quality)
- self._graph.setShadowQuality(sq)
-
- @Slot(int)
- def setBackgroundEnabled(self, enabled):
- self._graph.activeTheme().setBackgroundEnabled(enabled == Qt.Checked.value)
-
- @Slot(int)
- def setGridEnabled(self, enabled):
- self._graph.activeTheme().setGridEnabled(enabled == Qt.Checked.value)
-
- @Slot()
- def toggleItemCount(self):
- if self._itemCount == NUMBER_OF_ITEMS:
- self._itemCount = LOWER_NUMBER_OF_ITEMS
- self._CURVE_DIVIDER = LOWER_CURVE_DIVIDER
- else:
- self._itemCount = NUMBER_OF_ITEMS
- self._CURVE_DIVIDER = CURVE_DIVIDER
-
- self._graph.seriesList()[0].dataProxy().resetArray([])
- self.addData()
-
- @Slot()
- def toggleRanges(self):
- if not self._autoAdjust:
- self._graph.axisX().setAutoAdjustRange(True)
- self._graph.axisZ().setAutoAdjustRange(True)
- self._inputHandler.setDragSpeedModifier(1.5)
- self._autoAdjust = True
- else:
- self._graph.axisX().setRange(-10.0, 10.0)
- self._graph.axisZ().setRange(-10.0, 10.0)
- self._inputHandler.setDragSpeedModifier(15.0)
- self._autoAdjust = False
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import QObject, QSize, Qt
-from PySide6.QtWidgets import (QCheckBox, QComboBox, QCommandLinkButton,
- QLabel, QHBoxLayout, QSizePolicy,
- QVBoxLayout, QWidget, )
-from PySide6.QtQuickWidgets import QQuickWidget
-from PySide6.QtGraphs import (QAbstract3DSeries, Q3DScatter)
-
-from scatterdatamodifier import ScatterDataModifier
-
-
-class ScatterGraph(QObject):
-
- def __init__(self, minimum_graph_size, maximum_graph_size):
- super().__init__()
- self._scatterGraph = Q3DScatter()
- self._scatterWidget = QWidget()
- hLayout = QHBoxLayout(self._scatterWidget)
- self._scatterGraph.setMinimumSize(minimum_graph_size)
- self._scatterGraph.setMaximumSize(maximum_graph_size)
- self._scatterGraph.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- self._scatterGraph.setFocusPolicy(Qt.StrongFocus)
- self._scatterGraph.setResizeMode(QQuickWidget.SizeRootObjectToView)
- hLayout.addWidget(self._scatterGraph, 1)
-
- vLayout = QVBoxLayout()
- hLayout.addLayout(vLayout)
-
- cameraButton = QCommandLinkButton(self._scatterWidget)
- cameraButton.setText("Change camera preset")
- cameraButton.setDescription("Switch between a number of preset camera positions")
- cameraButton.setIconSize(QSize(0, 0))
-
- itemCountButton = QCommandLinkButton(self._scatterWidget)
- itemCountButton.setText("Toggle item count")
- itemCountButton.setDescription("Switch between 900 and 10000 data points")
- itemCountButton.setIconSize(QSize(0, 0))
-
- rangeButton = QCommandLinkButton(self._scatterWidget)
- rangeButton.setText("Toggle axis ranges")
- rangeButton.setDescription("Switch between automatic axis ranges and preset ranges")
- rangeButton.setIconSize(QSize(0, 0))
-
- backgroundCheckBox = QCheckBox(self._scatterWidget)
- backgroundCheckBox.setText("Show background")
- backgroundCheckBox.setChecked(True)
-
- gridCheckBox = QCheckBox(self._scatterWidget)
- gridCheckBox.setText("Show grid")
- gridCheckBox.setChecked(True)
-
- smoothCheckBox = QCheckBox(self._scatterWidget)
- smoothCheckBox.setText("Smooth dots")
- smoothCheckBox.setChecked(True)
-
- itemStyleList = QComboBox(self._scatterWidget)
- itemStyleList.addItem("Sphere", QAbstract3DSeries.Mesh.Sphere)
- itemStyleList.addItem("Cube", QAbstract3DSeries.Mesh.Cube)
- itemStyleList.addItem("Minimal", QAbstract3DSeries.Mesh.Minimal)
- itemStyleList.addItem("Point", QAbstract3DSeries.Mesh.Point)
- itemStyleList.setCurrentIndex(0)
-
- themeList = QComboBox(self._scatterWidget)
- themeList.addItem("Qt")
- themeList.addItem("Primary Colors")
- themeList.addItem("Digia")
- themeList.addItem("Stone Moss")
- themeList.addItem("Army Blue")
- themeList.addItem("Retro")
- themeList.addItem("Ebony")
- themeList.addItem("Isabelle")
- themeList.setCurrentIndex(3)
-
- shadowQuality = QComboBox(self._scatterWidget)
- shadowQuality.addItem("None")
- shadowQuality.addItem("Low")
- shadowQuality.addItem("Medium")
- shadowQuality.addItem("High")
- shadowQuality.addItem("Low Soft")
- shadowQuality.addItem("Medium Soft")
- shadowQuality.addItem("High Soft")
- shadowQuality.setCurrentIndex(6)
-
- vLayout.addWidget(cameraButton)
- vLayout.addWidget(itemCountButton)
- vLayout.addWidget(rangeButton)
- vLayout.addWidget(backgroundCheckBox)
- vLayout.addWidget(gridCheckBox)
- vLayout.addWidget(smoothCheckBox)
- vLayout.addWidget(QLabel("Change dot style"))
- vLayout.addWidget(itemStyleList)
- vLayout.addWidget(QLabel("Change theme"))
- vLayout.addWidget(themeList)
- vLayout.addWidget(QLabel("Adjust shadow quality"))
- vLayout.addWidget(shadowQuality, 1, Qt.AlignTop)
-
- self._modifier = ScatterDataModifier(self._scatterGraph, self)
-
- cameraButton.clicked.connect(self._modifier.changePresetCamera)
- itemCountButton.clicked.connect(self._modifier.toggleItemCount)
- rangeButton.clicked.connect(self._modifier.toggleRanges)
-
- backgroundCheckBox.stateChanged.connect(self._modifier.setBackgroundEnabled)
- gridCheckBox.stateChanged.connect(self._modifier.setGridEnabled)
- smoothCheckBox.stateChanged.connect(self._modifier.setSmoothDots)
-
- self._modifier.backgroundEnabledChanged.connect(backgroundCheckBox.setChecked)
- self._modifier.gridEnabledChanged.connect(gridCheckBox.setChecked)
- itemStyleList.currentIndexChanged.connect(self._modifier.changeStyle)
-
- themeList.currentIndexChanged.connect(self._modifier.changeTheme)
-
- shadowQuality.currentIndexChanged.connect(self._modifier.changeShadowQuality)
-
- self._modifier.shadowQualityChanged.connect(shadowQuality.setCurrentIndex)
- self._scatterGraph.shadowQualityChanged.connect(self._modifier.shadowQualityUpdatedByVisual)
-
- def scatterWidget(self):
- return self._scatterWidget
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from surfacegraphmodifier import SurfaceGraphModifier
-
-from PySide6.QtCore import QObject, Qt
-from PySide6.QtGui import QBrush, QIcon, QLinearGradient, QPainter, QPixmap
-from PySide6.QtWidgets import (QGroupBox, QCheckBox, QLabel, QHBoxLayout,
- QPushButton, QRadioButton, QSizePolicy, QSlider,
- QVBoxLayout, QWidget)
-from PySide6.QtQuickWidgets import QQuickWidget
-from PySide6.QtGraphs import Q3DSurface
-
-
-def gradientBtoYPB_Pixmap():
- grBtoY = QLinearGradient(0, 0, 1, 100)
- grBtoY.setColorAt(1.0, Qt.black)
- grBtoY.setColorAt(0.67, Qt.blue)
- grBtoY.setColorAt(0.33, Qt.red)
- grBtoY.setColorAt(0.0, Qt.yellow)
- pm = QPixmap(24, 100)
- with QPainter(pm) as pmp:
- pmp.setBrush(QBrush(grBtoY))
- pmp.setPen(Qt.NoPen)
- pmp.drawRect(0, 0, 24, 100)
- return pm
-
-
-def gradientGtoRPB_Pixmap():
- grGtoR = QLinearGradient(0, 0, 1, 100)
- grGtoR.setColorAt(1.0, Qt.darkGreen)
- grGtoR.setColorAt(0.5, Qt.yellow)
- grGtoR.setColorAt(0.2, Qt.red)
- grGtoR.setColorAt(0.0, Qt.darkRed)
- pm = QPixmap(24, 100)
- with QPainter(pm) as pmp:
- pmp.setBrush(QBrush(grGtoR))
- pmp.setPen(Qt.NoPen)
- pmp.drawRect(0, 0, 24, 100)
- return pm
-
-
-def highlightPixmap():
- HEIGHT = 400
- WIDTH = 110
- BORDER = 10
- gr = QLinearGradient(0, 0, 1, HEIGHT - 2 * BORDER)
- gr.setColorAt(1.0, Qt.black)
- gr.setColorAt(0.8, Qt.darkGreen)
- gr.setColorAt(0.6, Qt.green)
- gr.setColorAt(0.4, Qt.yellow)
- gr.setColorAt(0.2, Qt.red)
- gr.setColorAt(0.0, Qt.darkRed)
- pmHighlight = QPixmap(WIDTH, HEIGHT)
- pmHighlight.fill(Qt.transparent)
- with QPainter(pmHighlight) as pmpHighlight:
- pmpHighlight.setBrush(QBrush(gr))
- pmpHighlight.setPen(Qt.NoPen)
- pmpHighlight.drawRect(BORDER, BORDER, 35, HEIGHT - 2 * BORDER)
- pmpHighlight.setPen(Qt.black)
- step = (HEIGHT - 2 * BORDER) / 5
- for i in range(0, 6):
- yPos = i * step + BORDER
- pmpHighlight.drawLine(BORDER, yPos, 55, yPos)
- HEIGHT = 550 - (i * 110)
- pmpHighlight.drawText(60, yPos + 2, f"{HEIGHT} m")
- return pmHighlight
-
-
-class SurfaceGraph(QObject):
-
- def __init__(self, minimum_graph_size, maximum_graph_size):
- super().__init__()
- self._surfaceGraph = Q3DSurface()
- self._surfaceWidget = QWidget()
- hLayout = QHBoxLayout(self._surfaceWidget)
- self._surfaceGraph.setMinimumSize(minimum_graph_size)
- self._surfaceGraph.setMaximumSize(maximum_graph_size)
- self._surfaceGraph.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- self._surfaceGraph.setFocusPolicy(Qt.StrongFocus)
- self._surfaceGraph.setResizeMode(QQuickWidget.SizeRootObjectToView)
- hLayout.addWidget(self._surfaceGraph, 1)
- vLayout = QVBoxLayout()
- hLayout.addLayout(vLayout)
- vLayout.setAlignment(Qt.AlignTop)
- # Create control widgets
- modelGroupBox = QGroupBox("Model")
- sqrtSinModelRB = QRadioButton(self._surfaceWidget)
- sqrtSinModelRB.setText("Sqrt and Sin")
- sqrtSinModelRB.setChecked(False)
- heightMapModelRB = QRadioButton(self._surfaceWidget)
- heightMapModelRB.setText("Multiseries\nHeight Map")
- heightMapModelRB.setChecked(False)
- texturedModelRB = QRadioButton(self._surfaceWidget)
- texturedModelRB.setText("Textured\nTopography")
- texturedModelRB.setChecked(False)
- modelVBox = QVBoxLayout()
- modelVBox.addWidget(sqrtSinModelRB)
- modelVBox.addWidget(heightMapModelRB)
- modelVBox.addWidget(texturedModelRB)
- modelGroupBox.setLayout(modelVBox)
- selectionGroupBox = QGroupBox("Graph Selection Mode")
- modeNoneRB = QRadioButton(self._surfaceWidget)
- modeNoneRB.setText("No selection")
- modeNoneRB.setChecked(False)
- modeItemRB = QRadioButton(self._surfaceWidget)
- modeItemRB.setText("Item")
- modeItemRB.setChecked(False)
- modeSliceRowRB = QRadioButton(self._surfaceWidget)
- modeSliceRowRB.setText("Row Slice")
- modeSliceRowRB.setChecked(False)
- modeSliceColumnRB = QRadioButton(self._surfaceWidget)
- modeSliceColumnRB.setText("Column Slice")
- modeSliceColumnRB.setChecked(False)
- selectionVBox = QVBoxLayout()
- selectionVBox.addWidget(modeNoneRB)
- selectionVBox.addWidget(modeItemRB)
- selectionVBox.addWidget(modeSliceRowRB)
- selectionVBox.addWidget(modeSliceColumnRB)
- selectionGroupBox.setLayout(selectionVBox)
- axisGroupBox = QGroupBox("Axis ranges")
- axisMinSliderX = QSlider(Qt.Horizontal)
- axisMinSliderX.setMinimum(0)
- axisMinSliderX.setTickInterval(1)
- axisMinSliderX.setEnabled(True)
- axisMaxSliderX = QSlider(Qt.Horizontal)
- axisMaxSliderX.setMinimum(1)
- axisMaxSliderX.setTickInterval(1)
- axisMaxSliderX.setEnabled(True)
- axisMinSliderZ = QSlider(Qt.Horizontal)
- axisMinSliderZ.setMinimum(0)
- axisMinSliderZ.setTickInterval(1)
- axisMinSliderZ.setEnabled(True)
- axisMaxSliderZ = QSlider(Qt.Horizontal)
- axisMaxSliderZ.setMinimum(1)
- axisMaxSliderZ.setTickInterval(1)
- axisMaxSliderZ.setEnabled(True)
- axisVBox = QVBoxLayout(axisGroupBox)
- axisVBox.addWidget(QLabel("Column range"))
- axisVBox.addWidget(axisMinSliderX)
- axisVBox.addWidget(axisMaxSliderX)
- axisVBox.addWidget(QLabel("Row range"))
- axisVBox.addWidget(axisMinSliderZ)
- axisVBox.addWidget(axisMaxSliderZ)
- # Mode-dependent controls
- # sqrt-sin
- colorGroupBox = QGroupBox("Custom gradient")
-
- pixmap = gradientBtoYPB_Pixmap()
- gradientBtoYPB = QPushButton(self._surfaceWidget)
- gradientBtoYPB.setIcon(QIcon(pixmap))
- gradientBtoYPB.setIconSize(pixmap.size())
-
- pixmap = gradientGtoRPB_Pixmap()
- gradientGtoRPB = QPushButton(self._surfaceWidget)
- gradientGtoRPB.setIcon(QIcon(pixmap))
- gradientGtoRPB.setIconSize(pixmap.size())
-
- colorHBox = QHBoxLayout(colorGroupBox)
- colorHBox.addWidget(gradientBtoYPB)
- colorHBox.addWidget(gradientGtoRPB)
- # Multiseries heightmap
- showGroupBox = QGroupBox("Show Object")
- showGroupBox.setVisible(False)
- checkboxShowOilRigOne = QCheckBox("Oil Rig 1")
- checkboxShowOilRigOne.setChecked(True)
- checkboxShowOilRigTwo = QCheckBox("Oil Rig 2")
- checkboxShowOilRigTwo.setChecked(True)
- checkboxShowRefinery = QCheckBox("Refinery")
- showVBox = QVBoxLayout()
- showVBox.addWidget(checkboxShowOilRigOne)
- showVBox.addWidget(checkboxShowOilRigTwo)
- showVBox.addWidget(checkboxShowRefinery)
- showGroupBox.setLayout(showVBox)
- visualsGroupBox = QGroupBox("Visuals")
- visualsGroupBox.setVisible(False)
- checkboxVisualsSeeThrough = QCheckBox("See-Through")
- checkboxHighlightOil = QCheckBox("Highlight Oil")
- checkboxShowShadows = QCheckBox("Shadows")
- checkboxShowShadows.setChecked(True)
- visualVBox = QVBoxLayout(visualsGroupBox)
- visualVBox.addWidget(checkboxVisualsSeeThrough)
- visualVBox.addWidget(checkboxHighlightOil)
- visualVBox.addWidget(checkboxShowShadows)
- labelSelection = QLabel("Selection:")
- labelSelection.setVisible(False)
- labelSelectedItem = QLabel("Nothing")
- labelSelectedItem.setVisible(False)
- # Textured topography heightmap
- enableTexture = QCheckBox("Surface texture")
- enableTexture.setVisible(False)
-
- label = QLabel(self._surfaceWidget)
- label.setPixmap(highlightPixmap())
- heightMapGroupBox = QGroupBox("Highlight color map")
- colorMapVBox = QVBoxLayout()
- colorMapVBox.addWidget(label)
- heightMapGroupBox.setLayout(colorMapVBox)
- heightMapGroupBox.setVisible(False)
- # Populate vertical layout
- # Common
- vLayout.addWidget(modelGroupBox)
- vLayout.addWidget(selectionGroupBox)
- vLayout.addWidget(axisGroupBox)
- # Sqrt Sin
- vLayout.addWidget(colorGroupBox)
- # Multiseries heightmap
- vLayout.addWidget(showGroupBox)
- vLayout.addWidget(visualsGroupBox)
- vLayout.addWidget(labelSelection)
- vLayout.addWidget(labelSelectedItem)
- # Textured topography
- vLayout.addWidget(heightMapGroupBox)
- vLayout.addWidget(enableTexture)
- # Create the controller
- modifier = SurfaceGraphModifier(self._surfaceGraph, labelSelectedItem, self)
- # Connect widget controls to controller
- heightMapModelRB.toggled.connect(modifier.enableHeightMapModel)
- sqrtSinModelRB.toggled.connect(modifier.enableSqrtSinModel)
- texturedModelRB.toggled.connect(modifier.enableTopographyModel)
- modeNoneRB.toggled.connect(modifier.toggleModeNone)
- modeItemRB.toggled.connect(modifier.toggleModeItem)
- modeSliceRowRB.toggled.connect(modifier.toggleModeSliceRow)
- modeSliceColumnRB.toggled.connect(modifier.toggleModeSliceColumn)
- axisMinSliderX.valueChanged.connect(modifier.adjustXMin)
- axisMaxSliderX.valueChanged.connect(modifier.adjustXMax)
- axisMinSliderZ.valueChanged.connect(modifier.adjustZMin)
- axisMaxSliderZ.valueChanged.connect(modifier.adjustZMax)
- # Mode dependent connections
- gradientBtoYPB.pressed.connect(modifier.setBlackToYellowGradient)
- gradientGtoRPB.pressed.connect(modifier.setGreenToRedGradient)
- checkboxShowOilRigOne.stateChanged.connect(modifier.toggleItemOne)
- checkboxShowOilRigTwo.stateChanged.connect(modifier.toggleItemTwo)
- checkboxShowRefinery.stateChanged.connect(modifier.toggleItemThree)
- checkboxVisualsSeeThrough.stateChanged.connect(modifier.toggleSeeThrough)
- checkboxHighlightOil.stateChanged.connect(modifier.toggleOilHighlight)
- checkboxShowShadows.stateChanged.connect(modifier.toggleShadows)
- enableTexture.stateChanged.connect(modifier.toggleSurfaceTexture)
- # Connections to disable features depending on mode
- sqrtSinModelRB.toggled.connect(colorGroupBox.setVisible)
- heightMapModelRB.toggled.connect(showGroupBox.setVisible)
- heightMapModelRB.toggled.connect(visualsGroupBox.setVisible)
- heightMapModelRB.toggled.connect(labelSelection.setVisible)
- heightMapModelRB.toggled.connect(labelSelectedItem.setVisible)
- texturedModelRB.toggled.connect(enableTexture.setVisible)
- texturedModelRB.toggled.connect(heightMapGroupBox.setVisible)
- modifier.setAxisMinSliderX(axisMinSliderX)
- modifier.setAxisMaxSliderX(axisMaxSliderX)
- modifier.setAxisMinSliderZ(axisMinSliderZ)
- modifier.setAxisMaxSliderZ(axisMaxSliderZ)
- sqrtSinModelRB.setChecked(True)
- modeItemRB.setChecked(True)
- enableTexture.setChecked(True)
-
- def surfaceWidget(self):
- return self._surfaceWidget
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import os
-from math import sqrt, sin
-from pathlib import Path
-
-from PySide6.QtCore import QObject, QPropertyAnimation, Qt, Slot
-from PySide6.QtGui import (QColor, QFont, QImage, QLinearGradient,
- QQuaternion, QVector3D)
-from PySide6.QtGraphs import (QAbstract3DGraph, QCustom3DItem,
- QCustom3DLabel, QHeightMapSurfaceDataProxy,
- QValue3DAxis, QSurfaceDataItem,
- QSurfaceDataProxy, QSurface3DSeries,
- Q3DInputHandler, Q3DTheme)
-
-
-from highlightseries import HighlightSeries
-from topographicseries import TopographicSeries
-from custominputhandler import CustomInputHandler
-
-
-SAMPLE_COUNT_X = 150
-SAMPLE_COUNT_Z = 150
-HEIGHTMAP_GRID_STEP_X = 6
-HEIGHTMAP_GRID_STEP_Z = 6
-SAMPLE_MIN = -8.0
-SAMPLE_MAX = 8.0
-
-AREA_WIDTH = 8000.0
-AREA_HEIGHT = 8000.0
-ASPECT_RATIO = 0.1389
-MIN_RANGE = AREA_WIDTH * 0.49
-
-
-class SurfaceGraphModifier(QObject):
-
- def __init__(self, surface, label, parent):
- super().__init__(parent)
- self._data_path = Path(__file__).resolve().parent / "data"
- self._graph = surface
- self._textField = label
- self._sqrtSinProxy = None
- self._sqrtSinSeries = None
- self._heightMapProxyOne = None
- self._heightMapProxyTwo = None
- self._heightMapProxyThree = None
- self._heightMapSeriesOne = None
- self._heightMapSeriesTwo = None
- self._heightMapSeriesThree = None
-
- self._axisMinSliderX = None
- self._axisMaxSliderX = None
- self._axisMinSliderZ = None
- self._axisMaxSliderZ = None
- self._rangeMinX = 0.0
- self._rangeMinZ = 0.0
- self._stepX = 0.0
- self._stepZ = 0.0
- self._heightMapWidth = 0
- self._heightMapHeight = 0
-
- self._selectionAnimation = None
- self._titleLabel = None
- self._previouslyAnimatedItem = None
- self._previousScaling = {}
-
- self._topography = None
- self._highlight = None
- self._highlightWidth = 0
- self._highlightHeight = 0
-
- self._customInputHandler = None
- self._defaultInputHandler = Q3DInputHandler()
-
- self._graph.setCameraZoomLevel(85.0)
- self._graph.setCameraPreset(QAbstract3DGraph.CameraPreset.IsometricRight)
- self._graph.activeTheme().setType(Q3DTheme.Theme.Retro)
-
- self._x_axis = QValue3DAxis()
- self._y_axis = QValue3DAxis()
- self._z_axis = QValue3DAxis()
- self._graph.setAxisX(self._x_axis)
- self._graph.setAxisY(self._y_axis)
- self._graph.setAxisZ(self._z_axis)
-
- #
- # Sqrt Sin
- #
- self._sqrtSinProxy = QSurfaceDataProxy()
- self._sqrtSinSeries = QSurface3DSeries(self._sqrtSinProxy)
- self.fillSqrtSinProxy()
-
- #
- # Multisurface heightmap
- #
- # Create the first surface layer
- heightMapImageOne = QImage(self._data_path / "layer_1.png")
- self._heightMapProxyOne = QHeightMapSurfaceDataProxy(heightMapImageOne)
- self._heightMapSeriesOne = QSurface3DSeries(self._heightMapProxyOne)
- self._heightMapSeriesOne.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
- self._heightMapProxyOne.setValueRanges(34.0, 40.0, 18.0, 24.0)
-
- # Create the other 2 surface layers
- heightMapImageTwo = QImage(self._data_path / "layer_2.png")
- self._heightMapProxyTwo = QHeightMapSurfaceDataProxy(heightMapImageTwo)
- self._heightMapSeriesTwo = QSurface3DSeries(self._heightMapProxyTwo)
- self._heightMapSeriesTwo.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
- self._heightMapProxyTwo.setValueRanges(34.0, 40.0, 18.0, 24.0)
-
- heightMapImageThree = QImage(self._data_path / "layer_3.png")
- self._heightMapProxyThree = QHeightMapSurfaceDataProxy(heightMapImageThree)
- self._heightMapSeriesThree = QSurface3DSeries(self._heightMapProxyThree)
- self._heightMapSeriesThree.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
- self._heightMapProxyThree.setValueRanges(34.0, 40.0, 18.0, 24.0)
-
- # The images are the same size, so it's enough to get the dimensions
- # from one
- self._heightMapWidth = heightMapImageOne.width()
- self._heightMapHeight = heightMapImageOne.height()
-
- # Set the gradients for multi-surface layers
- grOne = QLinearGradient()
- grOne.setColorAt(0.0, Qt.black)
- grOne.setColorAt(0.38, Qt.darkYellow)
- grOne.setColorAt(0.39, Qt.darkGreen)
- grOne.setColorAt(0.5, Qt.darkGray)
- grOne.setColorAt(1.0, Qt.gray)
- self._heightMapSeriesOne.setBaseGradient(grOne)
- self._heightMapSeriesOne.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
-
- grTwo = QLinearGradient()
- grTwo.setColorAt(0.39, Qt.blue)
- grTwo.setColorAt(0.4, Qt.white)
- self._heightMapSeriesTwo.setBaseGradient(grTwo)
- self._heightMapSeriesTwo.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
-
- grThree = QLinearGradient()
- grThree.setColorAt(0.0, Qt.white)
- grThree.setColorAt(0.05, Qt.black)
- self._heightMapSeriesThree.setBaseGradient(grThree)
- self._heightMapSeriesThree.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
-
- # Custom items and label
- self._graph.selectedElementChanged.connect(self.handleElementSelected)
-
- self._selectionAnimation = QPropertyAnimation(self)
- self._selectionAnimation.setPropertyName(b"scaling")
- self._selectionAnimation.setDuration(500)
- self._selectionAnimation.setLoopCount(-1)
-
- titleFont = QFont("Century Gothic", 30)
- titleFont.setBold(True)
- self._titleLabel = QCustom3DLabel("Oil Rigs on Imaginary Sea", titleFont,
- QVector3D(0.0, 1.2, 0.0),
- QVector3D(1.0, 1.0, 0.0),
- QQuaternion())
- self._titleLabel.setPositionAbsolute(True)
- self._titleLabel.setFacingCamera(True)
- self._titleLabel.setBackgroundColor(QColor(0x66cdaa))
- self._graph.addCustomItem(self._titleLabel)
- self._titleLabel.setVisible(False)
-
- # Make two of the custom object visible
- self.toggleItemOne(True)
- self.toggleItemTwo(True)
-
- #
- # Topographic map
- #
- self._topography = TopographicSeries()
- file_name = os.fspath(self._data_path / "topography.png")
- self._topography.setTopographyFile(file_name, AREA_WIDTH, AREA_HEIGHT)
- self._topography.setItemLabelFormat("@yLabel m")
-
- self._highlight = HighlightSeries()
- self._highlight.setTopographicSeries(self._topography)
- self._highlight.setMinHeight(MIN_RANGE * ASPECT_RATIO)
- self._highlight.handleGradientChange(AREA_WIDTH * ASPECT_RATIO)
- self._graph.axisY().maxChanged.connect(self._highlight.handleGradientChange)
-
- self._customInputHandler = CustomInputHandler(self._graph)
- self._customInputHandler.setHighlightSeries(self._highlight)
- self._customInputHandler.setAxes(self._x_axis, self._y_axis, self._z_axis)
- self._customInputHandler.setLimits(0.0, AREA_WIDTH, MIN_RANGE)
- self._customInputHandler.setAspectRatio(ASPECT_RATIO)
-
- def fillSqrtSinProxy(self):
- stepX = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_X - 1)
- stepZ = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_Z - 1)
-
- dataArray = []
- for i in range(0, SAMPLE_COUNT_Z):
- newRow = []
- # Keep values within range bounds, since just adding step can
- # cause minor drift due to the rounding errors.
- z = min(SAMPLE_MAX, (i * stepZ + SAMPLE_MIN))
- for j in range(0, SAMPLE_COUNT_X):
- x = min(SAMPLE_MAX, (j * stepX + SAMPLE_MIN))
- R = sqrt(z * z + x * x) + 0.01
- y = (sin(R) / R + 0.24) * 1.61
- item = QSurfaceDataItem(QVector3D(x, y, z))
- newRow.append(item)
- dataArray.append(newRow)
- self._sqrtSinProxy.resetArray(dataArray)
-
- @Slot(bool)
- def enableSqrtSinModel(self, enable):
- if enable:
- self._sqrtSinSeries.setDrawMode(QSurface3DSeries.DrawSurfaceAndWireframe)
- self._sqrtSinSeries.setFlatShadingEnabled(True)
-
- self._graph.axisX().setLabelFormat("%.2f")
- self._graph.axisZ().setLabelFormat("%.2f")
- self._graph.axisX().setRange(SAMPLE_MIN, SAMPLE_MAX)
- self._graph.axisY().setRange(0.0, 2.0)
- self._graph.axisZ().setRange(SAMPLE_MIN, SAMPLE_MAX)
- self._graph.axisX().setLabelAutoRotation(30.0)
- self._graph.axisY().setLabelAutoRotation(90.0)
- self._graph.axisZ().setLabelAutoRotation(30.0)
-
- self._graph.removeSeries(self._heightMapSeriesOne)
- self._graph.removeSeries(self._heightMapSeriesTwo)
- self._graph.removeSeries(self._heightMapSeriesThree)
- self._graph.removeSeries(self._topography)
- self._graph.removeSeries(self._highlight)
-
- self._graph.addSeries(self._sqrtSinSeries)
-
- self._titleLabel.setVisible(False)
- self._graph.axisX().setTitleVisible(False)
- self._graph.axisY().setTitleVisible(False)
- self._graph.axisZ().setTitleVisible(False)
-
- self._graph.axisX().setTitle("")
- self._graph.axisY().setTitle("")
- self._graph.axisZ().setTitle("")
-
- self._graph.setActiveInputHandler(self._defaultInputHandler)
-
- # Reset range sliders for Sqrt & Sin
- self._rangeMinX = SAMPLE_MIN
- self._rangeMinZ = SAMPLE_MIN
- self._stepX = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_X - 1)
- self._stepZ = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_Z - 1)
- self._axisMinSliderX.setMinimum(0)
- self._axisMinSliderX.setMaximum(SAMPLE_COUNT_X - 2)
- self._axisMinSliderX.setValue(0)
- self._axisMaxSliderX.setMinimum(1)
- self._axisMaxSliderX.setMaximum(SAMPLE_COUNT_X - 1)
- self._axisMaxSliderX.setValue(SAMPLE_COUNT_X - 1)
- self._axisMinSliderZ.setMinimum(0)
- self._axisMinSliderZ.setMaximum(SAMPLE_COUNT_Z - 2)
- self._axisMinSliderZ.setValue(0)
- self._axisMaxSliderZ.setMinimum(1)
- self._axisMaxSliderZ.setMaximum(SAMPLE_COUNT_Z - 1)
- self._axisMaxSliderZ.setValue(SAMPLE_COUNT_Z - 1)
-
- @Slot(bool)
- def enableHeightMapModel(self, enable):
- if enable:
- self._heightMapSeriesOne.setDrawMode(QSurface3DSeries.DrawSurface)
- self._heightMapSeriesOne.setFlatShadingEnabled(False)
- self._heightMapSeriesTwo.setDrawMode(QSurface3DSeries.DrawSurface)
- self._heightMapSeriesTwo.setFlatShadingEnabled(False)
- self._heightMapSeriesThree.setDrawMode(QSurface3DSeries.DrawSurface)
- self._heightMapSeriesThree.setFlatShadingEnabled(False)
-
- self._graph.axisX().setLabelFormat("%.1f N")
- self._graph.axisZ().setLabelFormat("%.1f E")
- self._graph.axisX().setRange(34.0, 40.0)
- self._graph.axisY().setAutoAdjustRange(True)
- self._graph.axisZ().setRange(18.0, 24.0)
-
- self._graph.axisX().setTitle("Latitude")
- self._graph.axisY().setTitle("Height")
- self._graph.axisZ().setTitle("Longitude")
-
- self._graph.removeSeries(self._sqrtSinSeries)
- self._graph.removeSeries(self._topography)
- self._graph.removeSeries(self._highlight)
- self._graph.addSeries(self._heightMapSeriesOne)
- self._graph.addSeries(self._heightMapSeriesTwo)
- self._graph.addSeries(self._heightMapSeriesThree)
-
- self._graph.setActiveInputHandler(self._defaultInputHandler)
-
- self._titleLabel.setVisible(True)
- self._graph.axisX().setTitleVisible(True)
- self._graph.axisY().setTitleVisible(True)
- self._graph.axisZ().setTitleVisible(True)
-
- # Reset range sliders for height map
- mapGridCountX = self._heightMapWidth / HEIGHTMAP_GRID_STEP_X
- mapGridCountZ = self._heightMapHeight / HEIGHTMAP_GRID_STEP_Z
- self._rangeMinX = 34.0
- self._rangeMinZ = 18.0
- self._stepX = 6.0 / float(mapGridCountX - 1)
- self._stepZ = 6.0 / float(mapGridCountZ - 1)
- self._axisMinSliderX.setMinimum(0)
- self._axisMinSliderX.setMaximum(mapGridCountX - 2)
- self._axisMinSliderX.setValue(0)
- self._axisMaxSliderX.setMinimum(1)
- self._axisMaxSliderX.setMaximum(mapGridCountX - 1)
- self._axisMaxSliderX.setValue(mapGridCountX - 1)
- self._axisMinSliderZ.setMinimum(0)
- self._axisMinSliderZ.setMaximum(mapGridCountZ - 2)
- self._axisMinSliderZ.setValue(0)
- self._axisMaxSliderZ.setMinimum(1)
- self._axisMaxSliderZ.setMaximum(mapGridCountZ - 1)
- self._axisMaxSliderZ.setValue(mapGridCountZ - 1)
-
- @Slot(bool)
- def enableTopographyModel(self, enable):
- if enable:
- self._graph.axisX().setLabelFormat("%i")
- self._graph.axisZ().setLabelFormat("%i")
- self._graph.axisX().setRange(0.0, AREA_WIDTH)
- self._graph.axisY().setRange(100.0, AREA_WIDTH * ASPECT_RATIO)
- self._graph.axisZ().setRange(0.0, AREA_HEIGHT)
- self._graph.axisX().setLabelAutoRotation(30.0)
- self._graph.axisY().setLabelAutoRotation(90.0)
- self._graph.axisZ().setLabelAutoRotation(30.0)
-
- self._graph.removeSeries(self._heightMapSeriesOne)
- self._graph.removeSeries(self._heightMapSeriesTwo)
- self._graph.removeSeries(self._heightMapSeriesThree)
- self._graph.addSeries(self._topography)
- self._graph.addSeries(self._highlight)
-
- self._titleLabel.setVisible(False)
- self._graph.axisX().setTitleVisible(False)
- self._graph.axisY().setTitleVisible(False)
- self._graph.axisZ().setTitleVisible(False)
-
- self._graph.axisX().setTitle("")
- self._graph.axisY().setTitle("")
- self._graph.axisZ().setTitle("")
-
- self._graph.setActiveInputHandler(self._customInputHandler)
-
- # Reset range sliders for topography map
- self._rangeMinX = 0.0
- self._rangeMinZ = 0.0
- self._stepX = 1.0
- self._stepZ = 1.0
- self._axisMinSliderX.setMinimum(0)
- self._axisMinSliderX.setMaximum(AREA_WIDTH - 200)
- self._axisMinSliderX.setValue(0)
- self._axisMaxSliderX.setMinimum(200)
- self._axisMaxSliderX.setMaximum(AREA_WIDTH)
- self._axisMaxSliderX.setValue(AREA_WIDTH)
- self._axisMinSliderZ.setMinimum(0)
- self._axisMinSliderZ.setMaximum(AREA_HEIGHT - 200)
- self._axisMinSliderZ.setValue(0)
- self._axisMaxSliderZ.setMinimum(200)
- self._axisMaxSliderZ.setMaximum(AREA_HEIGHT)
- self._axisMaxSliderZ.setValue(AREA_HEIGHT)
-
- def adjustXMin(self, min):
- minX = self._stepX * float(min) + self._rangeMinX
-
- max = self._axisMaxSliderX.value()
- if min >= max:
- max = min + 1
- self._axisMaxSliderX.setValue(max)
-
- maxX = self._stepX * max + self._rangeMinX
-
- self.setAxisXRange(minX, maxX)
-
- def adjustXMax(self, max):
- maxX = self._stepX * float(max) + self._rangeMinX
-
- min = self._axisMinSliderX.value()
- if max <= min:
- min = max - 1
- self._axisMinSliderX.setValue(min)
-
- minX = self._stepX * min + self._rangeMinX
-
- self.setAxisXRange(minX, maxX)
-
- def adjustZMin(self, min):
- minZ = self._stepZ * float(min) + self._rangeMinZ
-
- max = self._axisMaxSliderZ.value()
- if min >= max:
- max = min + 1
- self._axisMaxSliderZ.setValue(max)
-
- maxZ = self._stepZ * max + self._rangeMinZ
-
- self.setAxisZRange(minZ, maxZ)
-
- def adjustZMax(self, max):
- maxX = self._stepZ * float(max) + self._rangeMinZ
-
- min = self._axisMinSliderZ.value()
- if max <= min:
- min = max - 1
- self._axisMinSliderZ.setValue(min)
-
- minX = self._stepZ * min + self._rangeMinZ
-
- self.setAxisZRange(minX, maxX)
-
- def setAxisXRange(self, min, max):
- self._graph.axisX().setRange(min, max)
-
- def setAxisZRange(self, min, max):
- self._graph.axisZ().setRange(min, max)
-
- def setBlackToYellowGradient(self):
- gr = QLinearGradient()
- gr.setColorAt(0.0, Qt.black)
- gr.setColorAt(0.33, Qt.blue)
- gr.setColorAt(0.67, Qt.red)
- gr.setColorAt(1.0, Qt.yellow)
-
- self._sqrtSinSeries.setBaseGradient(gr)
- self._sqrtSinSeries.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
-
- def setGreenToRedGradient(self):
- gr = QLinearGradient()
- gr.setColorAt(0.0, Qt.darkGreen)
- gr.setColorAt(0.5, Qt.yellow)
- gr.setColorAt(0.8, Qt.red)
- gr.setColorAt(1.0, Qt.darkRed)
-
- self._sqrtSinSeries.setBaseGradient(gr)
- self._sqrtSinSeries.setColorStyle(Q3DTheme.ColorStyle.RangeGradient)
-
- @Slot(bool)
- def toggleItemOne(self, show):
- positionOne = QVector3D(39.0, 77.0, 19.2)
- positionOnePipe = QVector3D(39.0, 45.0, 19.2)
- positionOneLabel = QVector3D(39.0, 107.0, 19.2)
- if show:
- color = QImage(2, 2, QImage.Format_RGB32)
- color.fill(Qt.red)
- file_name = os.fspath(self._data_path / "oilrig.mesh")
- item = QCustom3DItem(file_name, positionOne,
- QVector3D(0.025, 0.025, 0.025),
- QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 45.0),
- color)
- self._graph.addCustomItem(item)
- file_name = os.fspath(self._data_path / "pipe.mesh")
- item = QCustom3DItem(file_name, positionOnePipe,
- QVector3D(0.005, 0.5, 0.005), QQuaternion(),
- color)
- item.setShadowCasting(False)
- self._graph.addCustomItem(item)
-
- label = QCustom3DLabel()
- label.setText("Oil Rig One")
- label.setPosition(positionOneLabel)
- label.setScaling(QVector3D(1.0, 1.0, 1.0))
- self._graph.addCustomItem(label)
- else:
- self.resetSelection()
- self._graph.removeCustomItemAt(positionOne)
- self._graph.removeCustomItemAt(positionOnePipe)
- self._graph.removeCustomItemAt(positionOneLabel)
-
- @Slot(bool)
- def toggleItemTwo(self, show):
- positionTwo = QVector3D(34.5, 77.0, 23.4)
- positionTwoPipe = QVector3D(34.5, 45.0, 23.4)
- positionTwoLabel = QVector3D(34.5, 107.0, 23.4)
- if show:
- color = QImage(2, 2, QImage.Format_RGB32)
- color.fill(Qt.red)
- item = QCustom3DItem()
- file_name = os.fspath(self._data_path / "oilrig.mesh")
- item.setMeshFile(file_name)
- item.setPosition(positionTwo)
- item.setScaling(QVector3D(0.025, 0.025, 0.025))
- item.setRotation(QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 25.0))
- item.setTextureImage(color)
- self._graph.addCustomItem(item)
- file_name = os.fspath(self._data_path / "pipe.mesh")
- item = QCustom3DItem(file_name, positionTwoPipe,
- QVector3D(0.005, 0.5, 0.005), QQuaternion(),
- color)
- item.setShadowCasting(False)
- self._graph.addCustomItem(item)
-
- label = QCustom3DLabel()
- label.setText("Oil Rig Two")
- label.setPosition(positionTwoLabel)
- label.setScaling(QVector3D(1.0, 1.0, 1.0))
- self._graph.addCustomItem(label)
- else:
- self.resetSelection()
- self._graph.removeCustomItemAt(positionTwo)
- self._graph.removeCustomItemAt(positionTwoPipe)
- self._graph.removeCustomItemAt(positionTwoLabel)
-
- @Slot(bool)
- def toggleItemThree(self, show):
- positionThree = QVector3D(34.5, 86.0, 19.1)
- positionThreeLabel = QVector3D(34.5, 116.0, 19.1)
- if show:
- color = QImage(2, 2, QImage.Format_RGB32)
- color.fill(Qt.darkMagenta)
- item = QCustom3DItem()
- file_name = os.fspath(self._data_path / "refinery.mesh")
- item.setMeshFile(file_name)
- item.setPosition(positionThree)
- item.setScaling(QVector3D(0.04, 0.04, 0.04))
- item.setRotation(QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 75.0))
- item.setTextureImage(color)
- self._graph.addCustomItem(item)
-
- label = QCustom3DLabel()
- label.setText("Refinery")
- label.setPosition(positionThreeLabel)
- label.setScaling(QVector3D(1.0, 1.0, 1.0))
- self._graph.addCustomItem(label)
- else:
- self.resetSelection()
- self._graph.removeCustomItemAt(positionThree)
- self._graph.removeCustomItemAt(positionThreeLabel)
-
- @Slot(bool)
- def toggleSeeThrough(self, seethrough):
- s0 = self._graph.seriesList()[0]
- s1 = self._graph.seriesList()[1]
- if seethrough:
- s0.setDrawMode(QSurface3DSeries.DrawWireframe)
- s1.setDrawMode(QSurface3DSeries.DrawWireframe)
- else:
- s0.setDrawMode(QSurface3DSeries.DrawSurface)
- s1.setDrawMode(QSurface3DSeries.DrawSurface)
-
- @Slot(bool)
- def toggleOilHighlight(self, highlight):
- s2 = self._graph.seriesList()[2]
- if highlight:
- grThree = QLinearGradient()
- grThree.setColorAt(0.0, Qt.black)
- grThree.setColorAt(0.05, Qt.red)
- s2.setBaseGradient(grThree)
- else:
- grThree = QLinearGradient()
- grThree.setColorAt(0.0, Qt.white)
- grThree.setColorAt(0.05, Qt.black)
- s2.setBaseGradient(grThree)
-
- @Slot(bool)
- def toggleShadows(self, shadows):
- sq = (QAbstract3DGraph.ShadowQualityMedium
- if shadows else QAbstract3DGraph.ShadowQualityNone)
- self._graph.setShadowQuality(sq)
-
- @Slot(bool)
- def toggleSurfaceTexture(self, enable):
- if enable:
- file_name = os.fspath(self._data_path / "maptexture.jpg")
- self._topography.setTextureFile(file_name)
- else:
- self._topography.setTextureFile("")
-
- def handleElementSelected(self, type):
- self.resetSelection()
- if type == QAbstract3DGraph.ElementCustomItem:
- item = self._graph.selectedCustomItem()
- text = ""
- if isinstance(item, QCustom3DItem):
- text += "Custom label: "
- else:
- file = item.meshFile().split("/")[-1]
- text += f"{file}: "
-
- text += str(self._graph.selectedCustomItemIndex())
- self._textField.setText(text)
- self._previouslyAnimatedItem = item
- self._previousScaling = item.scaling()
- self._selectionAnimation.setTargetObject(item)
- self._selectionAnimation.setStartValue(item.scaling())
- self._selectionAnimation.setEndValue(item.scaling() * 1.5)
- self._selectionAnimation.start()
- elif type == QAbstract3DGraph.ElementSeries:
- text = "Surface ("
- series = self._graph.selectedSeries()
- if series:
- point = series.selectedPoint()
- text += f"{point.x()}, {point.y()}"
- text += ")"
- self._textField.setText(text)
- elif (type.value > QAbstract3DGraph.ElementSeries.value
- and type < QAbstract3DGraph.ElementCustomItem.value):
- index = self._graph.selectedLabelIndex()
- text = ""
- if type == QAbstract3DGraph.ElementAxisXLabel:
- text += "Axis X label: "
- elif type == QAbstract3DGraph.ElementAxisYLabel:
- text += "Axis Y label: "
- else:
- text += "Axis Z label: "
- text += str(index)
- self._textField.setText(text)
- else:
- self._textField.setText("Nothing")
-
- def resetSelection(self):
- self._selectionAnimation.stop()
- if self._previouslyAnimatedItem:
- self._previouslyAnimatedItem.setScaling(self._previousScaling)
- self._previouslyAnimatedItem = None
-
- def toggleModeNone(self):
- self._graph.setSelectionMode(QAbstract3DGraph.SelectionNone)
-
- def toggleModeItem(self):
- self._graph.setSelectionMode(QAbstract3DGraph.SelectionItem)
-
- def toggleModeSliceRow(self):
- sm = (QAbstract3DGraph.SelectionItemAndRow
- | QAbstract3DGraph.SelectionSlice
- | QAbstract3DGraph.SelectionMultiSeries)
- self._graph.setSelectionMode(sm)
-
- def toggleModeSliceColumn(self):
- sm = (QAbstract3DGraph.SelectionItemAndColumn
- | QAbstract3DGraph.SelectionSlice
- | QAbstract3DGraph.SelectionMultiSeries)
- self._graph.setSelectionMode(sm)
-
- def setAxisMinSliderX(self, slider):
- self._axisMinSliderX = slider
-
- def setAxisMaxSliderX(self, slider):
- self._axisMaxSliderX = slider
-
- def setAxisMinSliderZ(self, slider):
- self._axisMinSliderZ = slider
-
- def setAxisMaxSliderZ(self, slider):
- self._axisMaxSliderZ = slider
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import Qt
-from PySide6.QtGui import QImage, QVector3D
-from PySide6.QtGraphs import (QSurface3DSeries, QSurfaceDataItem)
-
-
-# Value used to encode height data as RGB value on PNG file
-PACKING_FACTOR = 11983.0
-
-
-class TopographicSeries(QSurface3DSeries):
-
- def __init__(self):
- super().__init__()
- self._sampleCountX = 0.0
- self._sampleCountZ = 0.0
- self.setDrawMode(QSurface3DSeries.DrawSurface)
- self.setFlatShadingEnabled(True)
- self.setBaseColor(Qt.white)
-
- def sampleCountX(self):
- return self._sampleCountX
-
- def sampleCountZ(self):
- return self._sampleCountZ
-
- def setTopographyFile(self, file, width, height):
- heightMapImage = QImage(file)
- bits = heightMapImage.bits()
- imageHeight = heightMapImage.height()
- imageWidth = heightMapImage.width()
- widthBits = imageWidth * 4
- stepX = width / float(imageWidth)
- stepZ = height / float(imageHeight)
-
- dataArray = []
- for i in range(0, imageHeight):
- p = i * widthBits
- z = height - float(i) * stepZ
- newRow = []
- for j in range(0, imageWidth):
- aa = bits[p + 0]
- rr = bits[p + 1]
- gg = bits[p + 2]
- color = (gg << 16) + (rr << 8) + aa
- y = float(color) / PACKING_FACTOR
- item = QSurfaceDataItem(QVector3D(float(j) * stepX, y, z))
- newRow.append(item)
- p += 4
- dataArray.append(newRow)
-
- self.dataProxy().resetArray(dataArray)
-
- self._sampleCountX = float(imageWidth)
- self._sampleCountZ = float(imageHeight)
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import QObject, Signal
-
-
-class VariantBarDataMapping(QObject):
-
- rowIndexChanged = Signal()
- columnIndexChanged = Signal()
- valueIndexChanged = Signal()
- rowCategoriesChanged = Signal()
- columnCategoriesChanged = Signal()
- mappingChanged = Signal()
-
- def __init__(self, rowIndex, columnIndex, valueIndex,
- rowCategories=[], columnCategories=[]):
- super().__init__(None)
- self._rowIndex = rowIndex
- self._columnIndex = columnIndex
- self._valueIndex = valueIndex
- self._rowCategories = rowCategories
- self._columnCategories = columnCategories
-
- def setRowIndex(self, index):
- self._rowIndex = index
- self.mappingChanged.emit()
-
- def rowIndex(self):
- return self._rowIndex
-
- def setColumnIndex(self, index):
- self._columnIndex = index
- self.mappingChanged.emit()
-
- def columnIndex(self):
- return self._columnIndex
-
- def setValueIndex(self, index):
- self._valueIndex = index
- self.mappingChanged.emit()
-
- def valueIndex(self):
- return self._valueIndex
-
- def setRowCategories(self, categories):
- self._rowCategories = categories
- self.mappingChanged.emit()
-
- def rowCategories(self):
- return self._rowCategories
-
- def setColumnCategories(self, categories):
- self._columnCategories = categories
- self.mappingChanged.emit()
-
- def columnCategories(self):
- return self._columnCategories
-
- def remap(self, rowIndex, columnIndex, valueIndex,
- rowCategories=[], columnCategories=[]):
- self._rowIndex = rowIndex
- self._columnIndex = columnIndex
- self._valueIndex = valueIndex
- self._rowCategories = rowCategories
- self._columnCategories = columnCategories
- self.mappingChanged.emit()
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import Slot
-from PySide6.QtGraphs import QBarDataProxy, QBarDataItem
-
-
-class VariantBarDataProxy(QBarDataProxy):
-
- def __init__(self):
- super().__init__()
- self._dataSet = None
- self._mapping = None
-
- def setDataSet(self, newSet):
- if self._dataSet:
- self._dataSet.itemsAdded.disconnect(self.handleItemsAdded)
- self._dataSet.dataCleared.disconnect(self.handleDataCleared)
-
- self._dataSet = newSet
-
- if self._dataSet:
- self._dataSet.itemsAdded.connect(self.handleItemsAdded)
- self._dataSet.dataCleared.connect(self.handleDataCleared)
- self.resolveDataSet()
-
- def dataSet(self):
- return self._dataSet.data()
-
- # Map key (row, column, value) to value index in data item (VariantItem).
- # Doesn't gain ownership of mapping, but does connect to it to listen for
- # mapping changes. Modifying mapping that is set to proxy will trigger
- # dataset re-resolving.
- def setMapping(self, mapping):
- if self._mapping:
- self._mapping.mappingChanged.disconnect(self.handleMappingChanged)
-
- self._mapping = mapping
-
- if self._mapping:
- self._mapping.mappingChanged.connect(self.handleMappingChanged)
-
- self.resolveDataSet()
-
- def mapping(self):
- return self._mapping.data()
-
- @Slot(int, int)
- def handleItemsAdded(self, index, count):
- # Resolve new items
- self.resolveDataSet()
-
- @Slot()
- def handleDataCleared(self):
- # Data cleared, reset array
- self.resetArray(None)
-
- @Slot()
- def handleMappingChanged(self):
- self.resolveDataSet()
-
- # Resolve entire dataset into QBarDataArray.
- def resolveDataSet(self):
- # If we have no data or mapping, or the categories are not defined,
- # simply clear the array
- if (not self._dataSet or not self._mapping
- or not self._mapping.rowCategories()
- or not self._mapping.columnCategories()):
- self.resetArray()
- return
-
- itemList = self._dataSet.itemList()
-
- rowIndex = self._mapping.rowIndex()
- columnIndex = self._mapping.columnIndex()
- valueIndex = self._mapping.valueIndex()
- rowList = self._mapping.rowCategories()
- columnList = self._mapping.columnCategories()
-
- # Sort values into rows and columns
- itemValueMap = {}
- for item in itemList:
- key = str(item[rowIndex])
- v = itemValueMap.get(key)
- if not v:
- v = {}
- itemValueMap[key] = v
- v[str(item[columnIndex])] = float(item[valueIndex])
-
- # Create a new data array in format the parent class understands
- newProxyArray = []
- for rowKey in rowList:
- newProxyRow = []
- for i in range(0, len(columnList)):
- item = QBarDataItem(itemValueMap[rowKey][columnList[i]])
- newProxyRow.append(item)
- newProxyArray.append(newProxyRow)
-
- # Finally, reset the data array in the parent class
- self.resetArray(newProxyArray)
+++ /dev/null
-# Copyright (C) 2023 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from PySide6.QtCore import QObject, Signal
-
-
-class VariantDataSet(QObject):
-
- itemsAdded = Signal(int, int)
- dataCleared = Signal()
-
- def __init__(self):
- super().__init__()
- self._variantData = []
-
- def clear(self):
- for item in self._variantData:
- item.clear()
- del item
-
- self._variantData.clear()
- self.dataCleared.emit()
-
- def addItem(self, item):
- self._variantData.append(item)
- addIndex = len(self._variantData)
-
- self.itemsAdded.emit(addIndex, 1)
- return addIndex
-
- def addItems(self, itemList):
- newCount = len(itemList)
- addIndex = len(self._variantData)
- self._variantData.extend(itemList)
- self.itemsAdded.emit(addIndex, newCount)
- return addIndex
-
- def itemList(self):
- return self._variantData
+++ /dev/null
-{
- "files": ["main.py",
- "axesinputhandler.py",
- "bargraph.py",
- "custominputhandler.py",
- "graphmodifier.py",
- "highlightseries.py",
- "rainfalldata.py",
- "scatterdatamodifier.py",
- "scattergraph.py",
- "surfacegraph.py",
- "surfacegraphmodifier.py",
- "topographicseries.py",
- "variantbardatamapping.py",
- "variantbardataproxy.py",
- "variantdataset.py",
- "data/layer_1.png",
- "data/layer_2.png",
- "data/layer_3.png",
- "data/license.txt",
- "data/maptexture.jpg",
- "data/narrowarrow.mesh",
- "data/oilrig.mesh",
- "data/pipe.mesh",
- "data/raindata.txt",
- "data/refinery.mesh",
- "data/topography.png"
-]
-}
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from graphmodifier import GraphModifier
+
+from PySide6.QtCore import QObject, Qt
+from PySide6.QtGui import QFont
+from PySide6.QtWidgets import (QButtonGroup, QCheckBox, QComboBox, QFontComboBox,
+ QLabel, QPushButton, QHBoxLayout, QSizePolicy,
+ QRadioButton, QSlider, QVBoxLayout, QWidget)
+from PySide6.QtQuickWidgets import QQuickWidget
+from PySide6.QtGraphs import QAbstract3DSeries, QtGraphs3D
+from PySide6.QtGraphsWidgets import Q3DBarsWidgetItem
+
+
+class BarGraph(QObject):
+
+ def __init__(self, minimum_graph_size, maximum_graph_size):
+ super().__init__()
+
+ barsGraph = Q3DBarsWidgetItem()
+ barsGraphWidget = QQuickWidget()
+ barsGraph.setWidget(barsGraphWidget)
+ self._barsWidget = QWidget()
+ hLayout = QHBoxLayout(self._barsWidget)
+ barsGraphWidget.setMinimumSize(minimum_graph_size)
+ barsGraphWidget.setMaximumSize(maximum_graph_size)
+ barsGraphWidget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
+ barsGraphWidget.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
+ hLayout.addWidget(barsGraphWidget, 1)
+
+ vLayout = QVBoxLayout()
+ hLayout.addLayout(vLayout)
+
+ themeList = QComboBox(self._barsWidget)
+ themeList.addItem("QtGreen")
+ themeList.addItem("QtGreenNeon")
+ themeList.addItem("MixSeries")
+ themeList.addItem("OrangeSeries")
+ themeList.addItem("YellowSeries")
+ themeList.addItem("BlueSeries")
+ themeList.addItem("PurpleSeries")
+ themeList.addItem("GreySeries")
+ themeList.setCurrentIndex(0)
+
+ labelButton = QPushButton(self._barsWidget)
+ labelButton.setText("Change label style")
+
+ smoothCheckBox = QCheckBox(self._barsWidget)
+ smoothCheckBox.setText("Smooth bars")
+ smoothCheckBox.setChecked(False)
+
+ barStyleList = QComboBox(self._barsWidget)
+ barStyleList.addItem("Bar", QAbstract3DSeries.Mesh.Bar)
+ barStyleList.addItem("Pyramid", QAbstract3DSeries.Mesh.Pyramid)
+ barStyleList.addItem("Cone", QAbstract3DSeries.Mesh.Cone)
+ barStyleList.addItem("Cylinder", QAbstract3DSeries.Mesh.Cylinder)
+ barStyleList.addItem("Bevel bar", QAbstract3DSeries.Mesh.BevelBar)
+ barStyleList.addItem("Sphere", QAbstract3DSeries.Mesh.Sphere)
+ barStyleList.setCurrentIndex(4)
+
+ cameraButton = QPushButton(self._barsWidget)
+ cameraButton.setText("Change camera preset")
+
+ zoomToSelectedButton = QPushButton(self._barsWidget)
+ zoomToSelectedButton.setText("Zoom to selected bar")
+
+ selectionModeList = QComboBox(self._barsWidget)
+ selectionModeList.addItem("None", QtGraphs3D.SelectionFlag.None_)
+ selectionModeList.addItem("Bar", QtGraphs3D.SelectionFlag.Item)
+ selectionModeList.addItem("Row", QtGraphs3D.SelectionFlag.Row)
+ sel = QtGraphs3D.SelectionFlag.ItemAndRow
+ selectionModeList.addItem("Bar and Row", sel)
+ selectionModeList.addItem("Column", QtGraphs3D.SelectionFlag.Column)
+ sel = QtGraphs3D.SelectionFlag.ItemAndColumn
+ selectionModeList.addItem("Bar and Column", sel)
+ sel = QtGraphs3D.SelectionFlag.RowAndColumn
+ selectionModeList.addItem("Row and Column", sel)
+ sel = QtGraphs3D.SelectionFlag.RowAndColumn
+ selectionModeList.addItem("Bar, Row and Column", sel)
+ sel = QtGraphs3D.SelectionFlag.Slice | QtGraphs3D.SelectionFlag.Row
+ selectionModeList.addItem("Slice into Row", sel)
+ sel = QtGraphs3D.SelectionFlag.Slice | QtGraphs3D.SelectionFlag.ItemAndRow
+ selectionModeList.addItem("Slice into Row and Item", sel)
+ sel = QtGraphs3D.SelectionFlag.Slice | QtGraphs3D.SelectionFlag.Column
+ selectionModeList.addItem("Slice into Column", sel)
+ sel = (QtGraphs3D.SelectionFlag.Slice
+ | QtGraphs3D.SelectionFlag.ItemAndColumn)
+ selectionModeList.addItem("Slice into Column and Item", sel)
+ sel = (QtGraphs3D.SelectionFlag.ItemRowAndColumn
+ | QtGraphs3D.SelectionFlag.MultiSeries)
+ selectionModeList.addItem("Multi: Bar, Row, Col", sel)
+ sel = (QtGraphs3D.SelectionFlag.Slice
+ | QtGraphs3D.SelectionFlag.ItemAndRow
+ | QtGraphs3D.SelectionFlag.MultiSeries)
+ selectionModeList.addItem("Multi, Slice: Row, Item", sel)
+ sel = (QtGraphs3D.SelectionFlag.Slice
+ | QtGraphs3D.SelectionFlag.ItemAndColumn
+ | QtGraphs3D.SelectionFlag.MultiSeries)
+ selectionModeList.addItem("Multi, Slice: Col, Item", sel)
+ selectionModeList.setCurrentIndex(1)
+
+ backgroundCheckBox = QCheckBox(self._barsWidget)
+ backgroundCheckBox.setText("Show background")
+ backgroundCheckBox.setChecked(False)
+
+ gridCheckBox = QCheckBox(self._barsWidget)
+ gridCheckBox.setText("Show grid")
+ gridCheckBox.setChecked(True)
+
+ seriesCheckBox = QCheckBox(self._barsWidget)
+ seriesCheckBox.setText("Show second series")
+ seriesCheckBox.setChecked(False)
+
+ reverseValueAxisCheckBox = QCheckBox(self._barsWidget)
+ reverseValueAxisCheckBox.setText("Reverse value axis")
+ reverseValueAxisCheckBox.setChecked(False)
+
+ rotationSliderX = QSlider(Qt.Orientation.Horizontal, self._barsWidget)
+ rotationSliderX.setTickInterval(30)
+ rotationSliderX.setTickPosition(QSlider.TickPosition.TicksBelow)
+ rotationSliderX.setMinimum(-180)
+ rotationSliderX.setValue(0)
+ rotationSliderX.setMaximum(180)
+ rotationSliderY = QSlider(Qt.Orientation.Horizontal, self._barsWidget)
+ rotationSliderY.setTickInterval(15)
+ rotationSliderY.setTickPosition(QSlider.TickPosition.TicksAbove)
+ rotationSliderY.setMinimum(-90)
+ rotationSliderY.setValue(0)
+ rotationSliderY.setMaximum(90)
+
+ fontSizeSlider = QSlider(Qt.Orientation.Horizontal, self._barsWidget)
+ fontSizeSlider.setTickInterval(10)
+ fontSizeSlider.setTickPosition(QSlider.TickPosition.TicksBelow)
+ fontSizeSlider.setMinimum(1)
+ fontSizeSlider.setValue(30)
+ fontSizeSlider.setMaximum(100)
+
+ fontList = QFontComboBox(self._barsWidget)
+ fontList.setCurrentFont(QFont("Times New Roman"))
+
+ shadowQuality = QComboBox(self._barsWidget)
+ shadowQuality.addItem("None")
+ shadowQuality.addItem("Low")
+ shadowQuality.addItem("Medium")
+ shadowQuality.addItem("High")
+ shadowQuality.addItem("Low Soft")
+ shadowQuality.addItem("Medium Soft")
+ shadowQuality.addItem("High Soft")
+ shadowQuality.setCurrentIndex(5)
+
+ rangeList = QComboBox(self._barsWidget)
+ rangeList.addItem("2015")
+ rangeList.addItem("2016")
+ rangeList.addItem("2017")
+ rangeList.addItem("2018")
+ rangeList.addItem("2019")
+ rangeList.addItem("2020")
+ rangeList.addItem("2021")
+ rangeList.addItem("2022")
+ rangeList.addItem("All")
+ rangeList.setCurrentIndex(8)
+
+ axisTitlesVisibleCB = QCheckBox(self._barsWidget)
+ axisTitlesVisibleCB.setText("Axis titles visible")
+ axisTitlesVisibleCB.setChecked(True)
+
+ axisTitlesFixedCB = QCheckBox(self._barsWidget)
+ axisTitlesFixedCB.setText("Axis titles fixed")
+ axisTitlesFixedCB.setChecked(True)
+
+ axisLabelRotationSlider = QSlider(Qt.Orientation.Horizontal, self._barsWidget)
+ axisLabelRotationSlider.setTickInterval(10)
+ axisLabelRotationSlider.setTickPosition(QSlider.TickPosition.TicksBelow)
+ axisLabelRotationSlider.setMinimum(0)
+ axisLabelRotationSlider.setValue(30)
+ axisLabelRotationSlider.setMaximum(90)
+
+ modeGroup = QButtonGroup(self._barsWidget)
+ modeWeather = QRadioButton("Temperature Data", self._barsWidget)
+ modeWeather.setChecked(True)
+ modeCustomProxy = QRadioButton("Custom Proxy Data", self._barsWidget)
+ modeGroup.addButton(modeWeather)
+ modeGroup.addButton(modeCustomProxy)
+
+ vLayout.addWidget(QLabel("Rotate horizontally"))
+ vLayout.addWidget(rotationSliderX, 0, Qt.AlignmentFlag.AlignTop)
+ vLayout.addWidget(QLabel("Rotate vertically"))
+ vLayout.addWidget(rotationSliderY, 0, Qt.AlignmentFlag.AlignTop)
+ vLayout.addWidget(labelButton, 0, Qt.AlignmentFlag.AlignTop)
+ vLayout.addWidget(cameraButton, 0, Qt.AlignmentFlag.AlignTop)
+ vLayout.addWidget(zoomToSelectedButton, 0, Qt.AlignmentFlag.AlignTop)
+ vLayout.addWidget(backgroundCheckBox)
+ vLayout.addWidget(gridCheckBox)
+ vLayout.addWidget(smoothCheckBox)
+ vLayout.addWidget(seriesCheckBox)
+ vLayout.addWidget(reverseValueAxisCheckBox)
+ vLayout.addWidget(axisTitlesVisibleCB)
+ vLayout.addWidget(axisTitlesFixedCB)
+ vLayout.addWidget(QLabel("Show year"))
+ vLayout.addWidget(rangeList)
+ vLayout.addWidget(QLabel("Change bar style"))
+ vLayout.addWidget(barStyleList)
+ vLayout.addWidget(QLabel("Change selection mode"))
+ vLayout.addWidget(selectionModeList)
+ vLayout.addWidget(QLabel("Change theme"))
+ vLayout.addWidget(themeList)
+ vLayout.addWidget(QLabel("Adjust shadow quality"))
+ vLayout.addWidget(shadowQuality)
+ vLayout.addWidget(QLabel("Change font"))
+ vLayout.addWidget(fontList)
+ vLayout.addWidget(QLabel("Adjust font size"))
+ vLayout.addWidget(fontSizeSlider)
+ vLayout.addWidget(QLabel("Axis label rotation"))
+ vLayout.addWidget(axisLabelRotationSlider, 0, Qt.AlignmentFlag.AlignTop)
+ vLayout.addWidget(modeWeather, 0, Qt.AlignmentFlag.AlignTop)
+ vLayout.addWidget(modeCustomProxy, 1, Qt.AlignmentFlag.AlignTop)
+
+ modifier = GraphModifier(barsGraph, self)
+ modifier.changeTheme(themeList.currentIndex())
+
+ rotationSliderX.valueChanged.connect(modifier.rotateX)
+ rotationSliderY.valueChanged.connect(modifier.rotateY)
+
+ labelButton.clicked.connect(modifier.changeLabelBackground)
+ cameraButton.clicked.connect(modifier.changePresetCamera)
+ zoomToSelectedButton.clicked.connect(modifier.zoomToSelectedBar)
+
+ backgroundCheckBox.checkStateChanged.connect(modifier.setPlotAreaBackgroundVisible)
+ gridCheckBox.checkStateChanged.connect(modifier.setGridVisible)
+ smoothCheckBox.checkStateChanged.connect(modifier.setSmoothBars)
+ seriesCheckBox.checkStateChanged.connect(modifier.setSeriesVisibility)
+ reverseValueAxisCheckBox.checkStateChanged.connect(modifier.setReverseValueAxis)
+
+ modifier.backgroundVisibleChanged.connect(backgroundCheckBox.setChecked)
+ modifier.gridVisibleChanged.connect(gridCheckBox.setChecked)
+
+ rangeList.currentIndexChanged.connect(modifier.changeRange)
+
+ barStyleList.currentIndexChanged.connect(modifier.changeStyle)
+
+ selectionModeList.currentIndexChanged.connect(modifier.changeSelectionMode)
+
+ themeList.currentIndexChanged.connect(modifier.changeTheme)
+
+ shadowQuality.currentIndexChanged.connect(modifier.changeShadowQuality)
+
+ modifier.shadowQualityChanged.connect(shadowQuality.setCurrentIndex)
+ barsGraph.shadowQualityChanged.connect(modifier.shadowQualityUpdatedByVisual)
+
+ fontSizeSlider.valueChanged.connect(modifier.changeFontSize)
+ fontList.currentFontChanged.connect(modifier.changeFont)
+
+ modifier.fontSizeChanged.connect(fontSizeSlider.setValue)
+ modifier.fontChanged.connect(fontList.setCurrentFont)
+
+ axisTitlesVisibleCB.checkStateChanged.connect(modifier.setAxisTitleVisibility)
+ axisTitlesFixedCB.checkStateChanged.connect(modifier.setAxisTitleFixed)
+ axisLabelRotationSlider.valueChanged.connect(modifier.changeLabelRotation)
+
+ modeWeather.toggled.connect(modifier.setDataModeToWeather)
+ modeCustomProxy.toggled.connect(modifier.setDataModeToCustom)
+ modeWeather.toggled.connect(seriesCheckBox.setEnabled)
+ modeWeather.toggled.connect(rangeList.setEnabled)
+ modeWeather.toggled.connect(axisTitlesVisibleCB.setEnabled)
+ modeWeather.toggled.connect(axisTitlesFixedCB.setEnabled)
+ modeWeather.toggled.connect(axisLabelRotationSlider.setEnabled)
+
+ def barsWidget(self):
+ return self._barsWidget
--- /dev/null
+License information regarding the data obtained from National Land Survey of
+Finland http://www.maanmittauslaitos.fi/en
+- topographic model from Elevation model 2 m (U4421B, U4421D, U4422A and
+ U4422C) 08/2014
+- map image extracted from Topographic map raster 1:50 000 (U442) 08/2014
+
+National Land Survey open data licence - version 1.0 - 1 May 2012
+
+1. General information
+
+The National Land Survey of Finland (hereinafter the Licensor), as the holder
+of the immaterial rights to the data, has granted on the terms mentioned below
+the right to use a copy (hereinafter data or dataset(s)) of the data (or a part
+of it).
+
+The Licensee is a natural or legal person who makes use of the data covered by
+this licence. The Licensee accepts the terms of this licence by receiving the
+dataset(s) covered by the licence.
+
+This Licence agreement does not create a co-operation or business relationship
+between the Licensee and the Licensor.
+
+2. Terms of the licence
+
+2.1. Right of use
+
+This licence grants a worldwide, free of charge and irrevocable parallel right
+of use to open data. According to the terms of the licence, data received by
+the Licensee can be freely:
+ - copied, distributed and published,
+ - modified and utilised commercially and non-commercially,
+ - inserted into other products and
+ - used as a part of a software application or service.
+
+2.2. Duties and responsibilities of the Licensee
+
+Through reasonable means suitable to the distribution medium or method which is
+used in conjunction with a product containing data or a service utilising data
+covered by this licence or while distributing data, the Licensee shall:
+ - mention the name of the Licensor, the name of the dataset(s) and the time
+ when the National Land Survey has delivered the dataset(s) (e.g.: contains
+ data from the National Land Survey of Finland Topographic Database 06/2012)
+ - provide a copy of this licence or a link to it, as well as
+ - require third parties to provide the same information when granting rights
+ to copies of dataset(s) or products and services containing such data and
+ - remove the name of the Licensor from the product or service, if required to
+ do so by the Licensor.
+
+The terms of this licence do not allow the Licensee to state in conjunction
+with the use of dataset(s) that the Licensor supports or recommends such use.
+
+2.3. Duties and responsibilities of the Licensor
+
+The Licensor shall ensure that
+ - the Licensor has the right to grant rights to the dataset(s) in accordance
+ with this licence.
+
+The data has been licensed "as is" and the Licensor
+ - shall not be held responsible for any errors or omissions in the data,
+ disclaims any warranty for the validity or up to date status of the data and
+ shall be free from liability for direct or consequential damages arising
+ from the use of data provided by the Licensor,
+ - and is not obligated to ensure the continuous availability of the data, nor
+ to announce in advance the interruption or cessation of availability, and
+ the Licensor shall be free from liability for direct or consequential
+ damages arising from any such interruption or cessation.
+
+3. Jurisdiction
+
+Finnish law shall apply to this licence.
+
+4. Changes to this licence
+
+The Licensor may at any time change the terms of the licence or apply a
+different licence to the data. The terms of this licence shall, however, still
+apply to such data that has been received prior to the change of the terms of
+the licence or the licence itself.
--- /dev/null
+# Rainfall per month from 2010 to 2022 in Northern Finland (Oulu)
+# Format: year, month, rainfall
+2010,1, 0,
+2010,2, 3.4,
+2010,3, 52,
+2010,4, 33.8,
+2010,5, 45.6,
+2010,6, 43.8,
+2010,7, 104.6,
+2010,8, 105.4,
+2010,9, 107.2,
+2010,10,38.6,
+2010,11,17.8,
+2010,12,0,
+2011,1, 8.2,
+2011,2, 1.6,
+2011,3, 27.4,
+2011,4, 15.8,
+2011,5, 57.6,
+2011,6, 85.2,
+2011,7, 127,
+2011,8, 72.2,
+2011,9, 82.2,
+2011,10,62.4,
+2011,11,31.6,
+2011,12,53.8,
+2012,1, 0,
+2012,2, 5,
+2012,3, 32.4,
+2012,4, 57.6,
+2012,5, 71.4,
+2012,6, 60.8,
+2012,7, 109,
+2012,8, 43.6,
+2012,9, 79.4,
+2012,10,117.2,
+2012,11,59,
+2012,12,0.2,
+2013,1, 28,
+2013,2, 19,
+2013,3, 0,
+2013,4, 37.6,
+2013,5, 44.2,
+2013,6, 104.8,
+2013,7, 84.2,
+2013,8, 57.2,
+2013,9, 37.2,
+2013,10,64.6,
+2013,11,77.8,
+2013,12,92.8,
+2014,1, 23.8,
+2014,2, 23.6,
+2014,3, 15.4,
+2014,4, 13.2,
+2014,5, 36.4,
+2014,6, 26.4,
+2014,7, 95.8,
+2014,8, 81.8,
+2014,9, 13.8,
+2014,10,94.6,
+2014,11,44.6,
+2014,12,31,
+2015,1, 37.4,
+2015,2, 21,
+2015,3, 42,
+2015,4, 8.8,
+2015,5, 82.4,
+2015,6, 150,
+2015,7, 56.8,
+2015,8, 67.2,
+2015,9, 131.2,
+2015,10,38.4,
+2015,11,83.4,
+2015,12,47.8,
+2016,1, 12.4,
+2016,2, 34.8,
+2016,3, 29,
+2016,4, 40.4,
+2016,5, 32.4,
+2016,6, 80.2,
+2016,7, 102.6,
+2016,8, 95.6,
+2016,9, 40.2,
+2016,10,7.8,
+2016,11,39.6,
+2016,12,8.8,
+2017,1, 9.4,
+2017,2, 6.6,
+2017,3, 29,
+2017,4, 46.2,
+2017,5, 43.2,
+2017,6, 25.2,
+2017,7, 72.4,
+2017,8, 58.8,
+2017,9, 68.8,
+2017,10,45.8,
+2017,11,36.8,
+2017,12,29.6,
+2018,1, 19.8,
+2018,2, 0.8,
+2018,3, 4,
+2018,4, 23.2,
+2018,5, 13.2,
+2018,6, 62.8,
+2018,7, 33,
+2018,8, 96.6,
+2018,9, 72.6,
+2018,10,48.8,
+2018,11,31.8,
+2018,12,12.8,
+2019,1, 0.2,
+2019,2, 24.8,
+2019,3, 32,
+2019,4, 8.8,
+2019,5, 71.4,
+2019,6, 65.8,
+2019,7, 17.6,
+2019,8, 90,
+2019,9, 50,
+2019,10,77,
+2019,11,27,
+2019,12,43.2,
+2020,1, 28.8,
+2020,2, 45,
+2020,3, 18.6,
+2020,4, 13,
+2020,5, 30.8,
+2020,6, 21.4,
+2020,7, 163.6,
+2020,8, 12,
+2020,9, 102.4,
+2020,10,133.2,
+2020,11,69.8,
+2020,12,40.6,
+2021,1, 0.4,
+2021,2, 21.6,
+2021,3, 24,
+2021,4, 51.4,
+2021,5, 76.4,
+2021,6, 29.2,
+2021,7, 36.4,
+2021,8, 116,
+2021,9, 72.4,
+2021,10,93.4,
+2021,11,21,
+2021,12,10.2,
+2022,1, 8.6,
+2022,2, 6.6,
+2022,3, 5.2,
+2022,4, 15.2,
+2022,5, 37.6,
+2022,6, 45,
+2022,7, 67.4,
+2022,8, 161.6,
+2022,9, 22.8,
+2022,10,75.2,
+2022,11,21.8,
+2022,12,0.2
--- /dev/null
+Widget Graph Gallery
+====================
+
+
+Widget Graph Gallery demonstrates all three graph types and some of their
+special features. The graphs have their own tabs in the application.
+
+
+.. image:: widgetgraphgallery.webp
+ :width: 400
+ :alt: Widget Screenshot
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+
+from math import atan, degrees
+import numpy as np
+
+from PySide6.QtCore import QObject, QPropertyAnimation, Qt, Signal, Slot
+from PySide6.QtGui import QFont, QVector3D
+from PySide6.QtGraphs import (QAbstract3DSeries,
+ QBarDataItem, QBar3DSeries, QCategory3DAxis,
+ QValue3DAxis, QtGraphs3D, QGraphsTheme)
+
+from rainfalldata import RainfallData
+
+# Set up data
+TEMP_OULU = np.array([
+ [-7.4, -2.4, 0.0, 3.0, 8.2, 11.6, 14.7, 15.4, 11.4, 4.2, 2.1, -2.3], # 2015
+ [-13.4, -3.9, -1.8, 3.1, 10.6, 13.7, 17.8, 13.6, 10.7, 3.5, -3.1, -4.2], # 2016
+ [-5.7, -6.7, -3.0, -0.1, 4.7, 12.4, 16.1, 14.1, 9.4, 3.0, -0.3, -3.2], # 2017
+ [-6.4, -11.9, -7.4, 1.9, 11.4, 12.4, 21.5, 16.1, 11.0, 4.4, 2.1, -4.1], # 2018
+ [-11.7, -6.1, -2.4, 3.9, 7.2, 14.5, 15.6, 14.4, 8.5, 2.0, -3.0, -1.5], # 2019
+ [-2.1, -3.4, -1.8, 0.6, 7.0, 17.1, 15.6, 15.4, 11.1, 5.6, 1.9, -1.7], # 2020
+ [-9.6, -11.6, -3.2, 2.4, 7.8, 17.3, 19.4, 14.2, 8.0, 5.2, -2.2, -8.6], # 2021
+ [-7.3, -6.4, -1.8, 1.3, 8.1, 15.5, 17.6, 17.6, 9.1, 5.4, -1.5, -4.4]], # 2022
+ np.float64)
+
+
+TEMP_HELSINKI = np.array([
+ [-2.0, -0.1, 1.8, 5.1, 9.7, 13.7, 16.3, 17.3, 12.7, 5.4, 4.6, 2.1], # 2015
+ [-10.3, -0.6, 0.0, 4.9, 14.3, 15.7, 17.7, 16.0, 12.7, 4.6, -1.0, -0.9], # 2016
+ [-2.9, -3.3, 0.7, 2.3, 9.9, 13.8, 16.1, 15.9, 11.4, 5.0, 2.7, 0.7], # 2017
+ [-2.2, -8.4, -4.7, 5.0, 15.3, 15.8, 21.2, 18.2, 13.3, 6.7, 2.8, -2.0], # 2018
+ [-6.2, -0.5, -0.3, 6.8, 10.6, 17.9, 17.5, 16.8, 11.3, 5.2, 1.8, 1.4], # 2019
+ [1.9, 0.5, 1.7, 4.5, 9.5, 18.4, 16.5, 16.8, 13.0, 8.2, 4.4, 0.9], # 2020
+ [-4.7, -8.1, -0.9, 4.5, 10.4, 19.2, 20.9, 15.4, 9.5, 8.0, 1.5, -6.7], # 2021
+ [-3.3, -2.2, -0.2, 3.3, 9.6, 16.9, 18.1, 18.9, 9.2, 7.6, 2.3, -3.4]], # 2022
+ np.float64)
+
+
+class GraphModifier(QObject):
+
+ shadowQualityChanged = Signal(int)
+ backgroundVisibleChanged = Signal(bool)
+ gridVisibleChanged = Signal(bool)
+ fontChanged = Signal(QFont)
+ fontSizeChanged = Signal(int)
+
+ def __init__(self, bargraph, parent):
+ super().__init__(parent)
+ self._graph = bargraph
+ self._temperatureAxis = QValue3DAxis()
+ self._yearAxis = QCategory3DAxis()
+ self._monthAxis = QCategory3DAxis()
+ self._primarySeries = QBar3DSeries()
+ self._secondarySeries = QBar3DSeries()
+ self._celsiusString = "°C"
+
+ self._xRotation = float(0)
+ self._yRotation = float(0)
+ self._fontSize = 30
+ self._segments = 4
+ self._subSegments = 3
+ self._minval = float(-20)
+ self._maxval = float(20)
+ self._barMesh = QAbstract3DSeries.Mesh.BevelBar
+ self._smooth = False
+ self._animationCameraX = QPropertyAnimation()
+ self._animationCameraY = QPropertyAnimation()
+ self._animationCameraZoom = QPropertyAnimation()
+ self._animationCameraTarget = QPropertyAnimation()
+ self._defaultAngleX = float(0)
+ self._defaultAngleY = float(0)
+ self._defaultZoom = float(0)
+ self._defaultTarget = []
+ self._customData = None
+
+ self._graph.setShadowQuality(QtGraphs3D.ShadowQuality.SoftMedium)
+ theme = self._graph.activeTheme()
+ theme.setPlotAreaBackgroundVisible(False)
+ theme.setLabelFont(QFont("Times New Roman", self._fontSize))
+ theme.setLabelBackgroundVisible(True)
+ self._graph.setMultiSeriesUniform(True)
+
+ self._months = ["January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November",
+ "December"]
+ self._years = ["2015", "2016", "2017", "2018", "2019", "2020",
+ "2021", "2022"]
+
+ self._temperatureAxis.setTitle("Average temperature")
+ self._temperatureAxis.setSegmentCount(self._segments)
+ self._temperatureAxis.setSubSegmentCount(self._subSegments)
+ self._temperatureAxis.setRange(self._minval, self._maxval)
+ self._temperatureAxis.setLabelFormat("%.1f " + self._celsiusString)
+ self._temperatureAxis.setLabelAutoAngle(30.0)
+ self._temperatureAxis.setTitleVisible(True)
+
+ self._yearAxis.setTitle("Year")
+ self._yearAxis.setLabelAutoAngle(30.0)
+ self._yearAxis.setTitleVisible(True)
+ self._monthAxis.setTitle("Month")
+ self._monthAxis.setLabelAutoAngle(30.0)
+ self._monthAxis.setTitleVisible(True)
+
+ self._graph.setValueAxis(self._temperatureAxis)
+ self._graph.setRowAxis(self._yearAxis)
+ self._graph.setColumnAxis(self._monthAxis)
+
+ format = "Oulu - @colLabel @rowLabel: @valueLabel"
+ self._primarySeries.setItemLabelFormat(format)
+ self._primarySeries.setMesh(QAbstract3DSeries.Mesh.BevelBar)
+ self._primarySeries.setMeshSmooth(False)
+
+ format = "Helsinki - @colLabel @rowLabel: @valueLabel"
+ self._secondarySeries.setItemLabelFormat(format)
+ self._secondarySeries.setMesh(QAbstract3DSeries.Mesh.BevelBar)
+ self._secondarySeries.setMeshSmooth(False)
+ self._secondarySeries.setVisible(False)
+
+ self._graph.addSeries(self._primarySeries)
+ self._graph.addSeries(self._secondarySeries)
+
+ self.changePresetCamera()
+
+ self.resetTemperatureData()
+
+ # Set up property animations for zooming to the selected bar
+ self._defaultAngleX = self._graph.cameraXRotation()
+ self._defaultAngleY = self._graph.cameraYRotation()
+ self._defaultZoom = self._graph.cameraZoomLevel()
+ self._defaultTarget = self._graph.cameraTargetPosition()
+
+ self._animationCameraX.setTargetObject(self._graph)
+ self._animationCameraY.setTargetObject(self._graph)
+ self._animationCameraZoom.setTargetObject(self._graph)
+ self._animationCameraTarget.setTargetObject(self._graph)
+
+ self._animationCameraX.setPropertyName(b"cameraXRotation")
+ self._animationCameraY.setPropertyName(b"cameraYRotation")
+ self._animationCameraZoom.setPropertyName(b"cameraZoomLevel")
+ self._animationCameraTarget.setPropertyName(b"cameraTargetPosition")
+
+ duration = 1700
+ self._animationCameraX.setDuration(duration)
+ self._animationCameraY.setDuration(duration)
+ self._animationCameraZoom.setDuration(duration)
+ self._animationCameraTarget.setDuration(duration)
+
+ # The zoom always first zooms out above the graph and then zooms in
+ zoomOutFraction = 0.3
+ self._animationCameraX.setKeyValueAt(zoomOutFraction, 0.0)
+ self._animationCameraY.setKeyValueAt(zoomOutFraction, 90.0)
+ self._animationCameraZoom.setKeyValueAt(zoomOutFraction, 50.0)
+ self._animationCameraTarget.setKeyValueAt(zoomOutFraction,
+ QVector3D(0, 0, 0))
+ self._customData = RainfallData()
+
+ def resetTemperatureData(self):
+ # Create data arrays
+ dataSet = []
+ dataSet2 = []
+
+ for year in range(0, len(self._years)):
+ # Create a data row
+ dataRow = []
+ dataRow2 = []
+ for month in range(0, len(self._months)):
+ # Add data to the row
+ item = QBarDataItem()
+ item.setValue(TEMP_OULU[year][month])
+ dataRow.append(item)
+ item = QBarDataItem()
+ item.setValue(TEMP_HELSINKI[year][month])
+ dataRow2.append(item)
+
+ # Add the row to the set
+ dataSet.append(dataRow)
+ dataSet2.append(dataRow2)
+
+ # Add data to the data proxy (the data proxy assumes ownership of it)
+ self._primarySeries.dataProxy().resetArray(dataSet, self._years, self._months)
+ self._secondarySeries.dataProxy().resetArray(dataSet2, self._years, self._months)
+
+ @Slot(int)
+ def changeRange(self, range):
+ if range >= len(self._years):
+ self._yearAxis.setRange(0, len(self._years) - 1)
+ else:
+ self._yearAxis.setRange(range, range)
+
+ @Slot(int)
+ def changeStyle(self, style):
+ comboBox = self.sender()
+ if comboBox:
+ self._barMesh = comboBox.itemData(style)
+ self._primarySeries.setMesh(self._barMesh)
+ self._secondarySeries.setMesh(self._barMesh)
+ self._customData.customSeries().setMesh(self._barMesh)
+
+ def changePresetCamera(self):
+ self._animationCameraX.stop()
+ self._animationCameraY.stop()
+ self._animationCameraZoom.stop()
+ self._animationCameraTarget.stop()
+
+ # Restore camera target in case animation has changed it
+ self._graph.setCameraTargetPosition(QVector3D(0.0, 0.0, 0.0))
+
+ self._preset = QtGraphs3D.CameraPreset.Front.value
+
+ self._graph.setCameraPreset(QtGraphs3D.CameraPreset(self._preset))
+
+ self._preset += 1
+ if self._preset > QtGraphs3D.CameraPreset.DirectlyBelow.value:
+ self._preset = QtGraphs3D.CameraPreset.FrontLow.value
+
+ @Slot(int)
+ def changeTheme(self, theme):
+ currentTheme = self._graph.activeTheme()
+ currentTheme.setTheme(QGraphsTheme.Theme(theme))
+ self.backgroundVisibleChanged.emit(currentTheme.isBackgroundVisible())
+ self.gridVisibleChanged.emit(currentTheme.isGridVisible())
+ self.fontChanged.emit(currentTheme.labelFont())
+ self.fontSizeChanged.emit(currentTheme.labelFont().pointSize())
+
+ def changeLabelBackground(self):
+ theme = self._graph.activeTheme()
+ theme.setLabelBackgroundVisible(not theme.isLabelBackgroundVisible())
+
+ @Slot(int)
+ def changeSelectionMode(self, selectionMode):
+ comboBox = self.sender()
+ if comboBox:
+ flags = comboBox.itemData(selectionMode)
+ self._graph.setSelectionMode(QtGraphs3D.SelectionFlags(flags))
+
+ def changeFont(self, font):
+ newFont = font
+ self._graph.activeTheme().setLabelFont(newFont)
+
+ def changeFontSize(self, fontsize):
+ self._fontSize = fontsize
+ font = self._graph.activeTheme().labelFont()
+ font.setPointSize(self._fontSize)
+ self._graph.activeTheme().setLabelFont(font)
+
+ @Slot(QtGraphs3D.ShadowQuality)
+ def shadowQualityUpdatedByVisual(self, sq):
+ # Updates the UI component to show correct shadow quality
+ self.shadowQualityChanged.emit(sq.value)
+
+ @Slot(int)
+ def changeLabelRotation(self, rotation):
+ self._temperatureAxis.setLabelAutoAngle(float(rotation))
+ self._monthAxis.setLabelAutoAngle(float(rotation))
+ self._yearAxis.setLabelAutoAngle(float(rotation))
+
+ @Slot(bool)
+ def setAxisTitleVisibility(self, state):
+ enabled = state == Qt.CheckState.Checked
+ self._temperatureAxis.setTitleVisible(enabled)
+ self._monthAxis.setTitleVisible(enabled)
+ self._yearAxis.setTitleVisible(enabled)
+
+ @Slot(bool)
+ def setAxisTitleFixed(self, state):
+ enabled = state == Qt.CheckState.Checked
+ self._temperatureAxis.setTitleFixed(enabled)
+ self._monthAxis.setTitleFixed(enabled)
+ self._yearAxis.setTitleFixed(enabled)
+
+ @Slot()
+ def zoomToSelectedBar(self):
+ self._animationCameraX.stop()
+ self._animationCameraY.stop()
+ self._animationCameraZoom.stop()
+ self._animationCameraTarget.stop()
+
+ currentX = self._graph.cameraXRotation()
+ currentY = self._graph.cameraYRotation()
+ currentZoom = self._graph.cameraZoomLevel()
+ currentTarget = self._graph.cameraTargetPosition()
+
+ self._animationCameraX.setStartValue(currentX)
+ self._animationCameraY.setStartValue(currentY)
+ self._animationCameraZoom.setStartValue(currentZoom)
+ self._animationCameraTarget.setStartValue(currentTarget)
+
+ selectedBar = (self._graph.selectedSeries().selectedBar()
+ if self._graph.selectedSeries()
+ else QBar3DSeries.invalidSelectionPosition())
+
+ if selectedBar != QBar3DSeries.invalidSelectionPosition():
+ # Normalize selected bar position within axis range to determine
+ # target coordinates
+ endTarget = QVector3D()
+ xMin = self._graph.columnAxis().min()
+ xRange = self._graph.columnAxis().max() - xMin
+ zMin = self._graph.rowAxis().min()
+ zRange = self._graph.rowAxis().max() - zMin
+ endTarget.setX((selectedBar.y() - xMin) / xRange * 2.0 - 1.0)
+ endTarget.setZ((selectedBar.x() - zMin) / zRange * 2.0 - 1.0)
+
+ # Rotate the camera so that it always points approximately to the
+ # graph center
+ endAngleX = 90.0 - degrees(atan(float(endTarget.z() / endTarget.x())))
+ if endTarget.x() > 0.0:
+ endAngleX -= 180.0
+ proxy = self._graph.selectedSeries().dataProxy()
+ barValue = proxy.itemAt(selectedBar.x(), selectedBar.y()).value()
+ endAngleY = 30.0 if barValue >= 0.0 else -30.0
+ if self._graph.valueAxis().reversed():
+ endAngleY *= -1.0
+
+ self._animationCameraX.setEndValue(float(endAngleX))
+ self._animationCameraY.setEndValue(endAngleY)
+ self._animationCameraZoom.setEndValue(250)
+ self._animationCameraTarget.setEndValue(endTarget)
+ else:
+ # No selected bar, so return to the default view
+ self._animationCameraX.setEndValue(self._defaultAngleX)
+ self._animationCameraY.setEndValue(self._defaultAngleY)
+ self._animationCameraZoom.setEndValue(self._defaultZoom)
+ self._animationCameraTarget.setEndValue(self._defaultTarget)
+
+ self._animationCameraX.start()
+ self._animationCameraY.start()
+ self._animationCameraZoom.start()
+ self._animationCameraTarget.start()
+
+ @Slot(bool)
+ def setDataModeToWeather(self, enabled):
+ if enabled:
+ self.changeDataMode(False)
+
+ @Slot(bool)
+ def setDataModeToCustom(self, enabled):
+ if enabled:
+ self.changeDataMode(True)
+
+ def changeShadowQuality(self, quality):
+ sq = QtGraphs3D.ShadowQuality(quality)
+ self._graph.setShadowQuality(sq)
+ self.shadowQualityChanged.emit(quality)
+
+ def rotateX(self, rotation):
+ self._xRotation = rotation
+ self._graph.setCameraPosition(self._xRotation, self._yRotation)
+
+ def rotateY(self, rotation):
+ self._yRotation = rotation
+ self._graph.setCameraPosition(self._xRotation, self._yRotation)
+
+ def setPlotAreaBackgroundVisible(self, state):
+ enabled = state == Qt.CheckState.Checked
+ self._graph.activeTheme().setPlotAreaBackgroundVisible(enabled)
+
+ def setGridVisible(self, state):
+ self._graph.activeTheme().setGridVisible(state == Qt.CheckState.Checked)
+
+ def setSmoothBars(self, state):
+ self._smooth = state == Qt.CheckState.Checked
+ self._primarySeries.setMeshSmooth(self._smooth)
+ self._secondarySeries.setMeshSmooth(self._smooth)
+ self._customData.customSeries().setMeshSmooth(self._smooth)
+
+ def setSeriesVisibility(self, state):
+ self._secondarySeries.setVisible(state == Qt.CheckState.Checked)
+
+ def setReverseValueAxis(self, state):
+ self._graph.valueAxis().setReversed(state == Qt.CheckState.Checked)
+
+ def changeDataMode(self, customData):
+ # Change between weather data and data from custom proxy
+ if customData:
+ self._graph.removeSeries(self._primarySeries)
+ self._graph.removeSeries(self._secondarySeries)
+ self._graph.addSeries(self._customData.customSeries())
+ self._graph.setValueAxis(self._customData.valueAxis())
+ self._graph.setRowAxis(self._customData.rowAxis())
+ self._graph.setColumnAxis(self._customData.colAxis())
+ else:
+ self._graph.removeSeries(self._customData.customSeries())
+ self._graph.addSeries(self._primarySeries)
+ self._graph.addSeries(self._secondarySeries)
+ self._graph.setValueAxis(self._temperatureAxis)
+ self._graph.setRowAxis(self._yearAxis)
+ self._graph.setColumnAxis(self._monthAxis)
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from PySide6.QtCore import QPoint, Qt, Slot
+from PySide6.QtGui import QLinearGradient, QVector3D
+from PySide6.QtGraphs import (QSurface3DSeries, QSurfaceDataItem,
+ QGraphsTheme)
+
+
+DARK_RED_POS = 1.0
+RED_POS = 0.8
+YELLOW_POS = 0.6
+GREEN_POS = 0.4
+DARK_GREEN_POS = 0.2
+
+
+class HighlightSeries(QSurface3DSeries):
+
+ def __init__(self):
+ super().__init__()
+ self._width = 100
+ self._height = 100
+ self._srcWidth = 0
+ self._srcHeight = 0
+ self._position = {}
+ self._topographicSeries = None
+ self._minHeight = 0.0
+ self.setDrawMode(QSurface3DSeries.DrawFlag.DrawSurface)
+ self.setShading(QSurface3DSeries.Shading.Flat)
+ self.setVisible(False)
+
+ def setTopographicSeries(self, series):
+ self._topographicSeries = series
+ array = self._topographicSeries.dataArray()
+ self._srcWidth = len(array[0])
+ self._srcHeight = len(array)
+ self._topographicSeries.selectedPointChanged.connect(self.handlePositionChange)
+
+ def setMinHeight(self, height):
+ self. m_minHeight = height
+
+ @Slot(QPoint)
+ def handlePositionChange(self, position):
+ self._position = position
+
+ if position == self.invalidSelectionPosition():
+ self.setVisible(False)
+ return
+
+ halfWidth = self._width / 2
+ halfHeight = self._height / 2
+
+ startX = position.y() - halfWidth
+ if startX < 0:
+ startX = 0
+ endX = position.y() + halfWidth
+ if endX > (self._srcWidth - 1):
+ endX = self._srcWidth - 1
+ startZ = position.x() - halfHeight
+ if startZ < 0:
+ startZ = 0
+ endZ = position.x() + halfHeight
+ if endZ > (self._srcHeight - 1):
+ endZ = self._srcHeight - 1
+
+ srcArray = self._topographicSeries.dataArray()
+
+ dataArray = []
+ for i in range(int(startZ), int(endZ)):
+ newRow = []
+ srcRow = srcArray[i]
+ for j in range(startX, endX):
+ pos = srcRow.at(j).position()
+ pos.setY(pos.y() + 0.1)
+ item = QSurfaceDataItem(QVector3D(pos))
+ newRow.append(item)
+ dataArray.append(newRow)
+ self.dataProxy().resetArray(dataArray)
+ self.setVisible(True)
+
+ @Slot(float)
+ def handleGradientChange(self, value):
+ ratio = self._minHeight / value
+
+ gr = QLinearGradient()
+ gr.setColorAt(0.0, Qt.GlobalColor.black)
+ gr.setColorAt(DARK_GREEN_POS * ratio, Qt.GlobalColor.darkGreen)
+ gr.setColorAt(GREEN_POS * ratio, Qt.GlobalColor.green)
+ gr.setColorAt(YELLOW_POS * ratio, Qt.GlobalColor.yellow)
+ gr.setColorAt(RED_POS * ratio, Qt.GlobalColor.red)
+ gr.setColorAt(DARK_RED_POS * ratio, Qt.GlobalColor.darkRed)
+
+ self.setBaseGradient(gr)
+ self.setColorStyle(QGraphsTheme.ColorStyle.RangeGradient)
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+"""PySide6 port of the Qt Graphs widgetgallery example from Qt v6.x"""
+
+import sys
+
+from PySide6.QtCore import QSize
+from PySide6.QtWidgets import QApplication, QTabWidget
+
+from bargraph import BarGraph
+from scattergraph import ScatterGraph
+from surfacegraph import SurfaceGraph
+
+
+class MainWidget(QTabWidget):
+ """Tab widget for creating own tabs for Q3DBars, Q3DScatter, and Q3DSurface"""
+
+ def __init__(self, p=None):
+ super().__init__(p)
+
+ screen_size = self.screen().size()
+ minimum_graph_size = QSize(screen_size.width() / 2, screen_size.height() / 1.75)
+
+ # Create bar graph
+ self._bars = BarGraph(minimum_graph_size, screen_size)
+ # Create scatter graph
+ self._scatter = ScatterGraph(minimum_graph_size, screen_size)
+ # Create surface graph
+ self._surface = SurfaceGraph(minimum_graph_size, screen_size)
+
+ # Add bars widget
+ self.addTab(self._bars.barsWidget(), "Bar Graph")
+ # Add scatter widget
+ self.addTab(self._scatter.scatterWidget(), "Scatter Graph")
+ # Add surface widget
+ self.addTab(self._surface.surfaceWidget(), "Surface Graph")
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+
+ tabWidget = MainWidget()
+ tabWidget.setWindowTitle("Widget Gallery")
+
+ tabWidget.show()
+ exit_code = app.exec()
+ del tabWidget
+ sys.exit(exit_code)
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+import sys
+
+from pathlib import Path
+
+from PySide6.QtCore import QFile, QIODevice, QObject
+from PySide6.QtGraphs import (QBar3DSeries, QCategory3DAxis, QValue3DAxis)
+
+from variantbardataproxy import VariantBarDataProxy
+from variantbardatamapping import VariantBarDataMapping
+from variantdataset import VariantDataSet
+
+
+MONTHS = ["January", "February", "March", "April",
+ "May", "June", "July", "August", "September", "October",
+ "November", "December"]
+
+
+class RainfallData(QObject):
+
+ def __init__(self):
+ super().__init__()
+ self._columnCount = 0
+ self._rowCount = 0
+ self._years = []
+ self._numericMonths = []
+ self._proxy = VariantBarDataProxy()
+ self._mapping = None
+ self._dataSet = None
+ self._series = QBar3DSeries()
+ self._valueAxis = QValue3DAxis()
+ self._rowAxis = QCategory3DAxis()
+ self._colAxis = QCategory3DAxis()
+
+ # In data file the months are in numeric format, so create custom list
+ for i in range(1, 13):
+ self._numericMonths.append(str(i))
+
+ self._columnCount = len(self._numericMonths)
+
+ self.updateYearsList(2010, 2022)
+
+ # Create proxy and series
+ self._proxy = VariantBarDataProxy()
+ self._series = QBar3DSeries(self._proxy)
+
+ self._series.setItemLabelFormat("%.1f mm")
+
+ # Create the axes
+ self._rowAxis = QCategory3DAxis(self)
+ self._colAxis = QCategory3DAxis(self)
+ self._valueAxis = QValue3DAxis(self)
+ self._rowAxis.setAutoAdjustRange(True)
+ self._colAxis.setAutoAdjustRange(True)
+ self._valueAxis.setAutoAdjustRange(True)
+
+ # Set axis labels and titles
+ self._rowAxis.setTitle("Year")
+ self._colAxis.setTitle("Month")
+ self._valueAxis.setTitle("rainfall (mm)")
+ self._valueAxis.setSegmentCount(5)
+ self._rowAxis.setLabels(self._years)
+ self._colAxis.setLabels(MONTHS)
+ self._rowAxis.setTitleVisible(True)
+ self._colAxis.setTitleVisible(True)
+ self._valueAxis.setTitleVisible(True)
+
+ self.addDataSet()
+
+ def customSeries(self):
+ return self._series
+
+ def valueAxis(self):
+ return self._valueAxis
+
+ def rowAxis(self):
+ return self._rowAxis
+
+ def colAxis(self):
+ return self._colAxis
+
+ def updateYearsList(self, start, end):
+ self._years.clear()
+ for i in range(start, end + 1):
+ self._years.append(str(i))
+ self._rowCount = len(self._years)
+
+ def addDataSet(self):
+ # Create a new variant data set and data item list
+ self._dataSet = VariantDataSet()
+ itemList = []
+
+ # Read data from a data file into the data item list
+ file_path = Path(__file__).resolve().parent / "data" / "raindata.txt"
+ dataFile = QFile(file_path)
+ if dataFile.open(QIODevice.OpenModeFlag.ReadOnly | QIODevice.OpenModeFlag.Text):
+ data = dataFile.readAll().data().decode("utf8")
+ for line in data.split("\n"):
+ if line and not line.startswith("#"): # Ignore comments
+ tokens = line.split(",")
+ # Each line has three data items: Year, month, and
+ # rainfall value
+ if len(tokens) >= 3:
+ # Store year and month as strings, and rainfall value
+ # as double into a variant data item and add the item to
+ # the item list.
+ newItem = []
+ newItem.append(tokens[0].strip())
+ newItem.append(tokens[1].strip())
+ newItem.append(float(tokens[2].strip()))
+ itemList.append(newItem)
+ else:
+ print("Unable to open data file:", dataFile.fileName(),
+ file=sys.stderr)
+
+ # Add items to the data set and set it to the proxy
+ self._dataSet.addItems(itemList)
+ self._proxy.setDataSet(self._dataSet)
+
+ # Create new mapping for the data and set it to the proxy
+ self._mapping = VariantBarDataMapping(0, 1, 2,
+ self._years, self._numericMonths)
+ self._proxy.setMapping(self._mapping)
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from enum import Enum
+from math import sin, cos, degrees, sqrt
+
+from PySide6.QtCore import QObject, Signal, Slot, Qt
+from PySide6.QtGui import QVector2D, QVector3D
+from PySide6.QtGraphs import (QAbstract3DSeries,
+ QScatterDataItem, QScatterDataProxy,
+ QScatter3DSeries, QtGraphs3D, QGraphsTheme)
+
+
+NUMBER_OF_ITEMS = 10000
+CURVE_DIVIDER = 7.5
+LOWER_NUMBER_OF_ITEMS = 900
+LOWER_CURVE_DIVIDER = 0.75
+
+
+class InputState(Enum):
+ StateNormal = 0
+ StateDraggingX = 1
+ StateDraggingZ = 2
+ StateDraggingY = 3
+
+
+class ScatterDataModifier(QObject):
+
+ backgroundEnabledChanged = Signal(bool)
+ gridVisibleChanged = Signal(bool)
+ shadowQualityChanged = Signal(int)
+
+ def __init__(self, scatter, parent):
+ super().__init__(parent)
+
+ self._graph = scatter
+
+ self._style = QAbstract3DSeries.Mesh.Sphere
+ self._smooth = True
+ self._autoAdjust = True
+ self._itemCount = LOWER_NUMBER_OF_ITEMS
+ self._CURVE_DIVIDER = LOWER_CURVE_DIVIDER
+
+ self._graph.activeTheme().setTheme(QGraphsTheme.Theme.MixSeries)
+ self._graph.activeTheme().setColorScheme(QGraphsTheme.ColorScheme.Dark)
+ self._graph.setShadowQuality(QtGraphs3D.ShadowQuality.SoftHigh)
+ self._graph.setCameraPreset(QtGraphs3D.CameraPreset.Front)
+ self._graph.setCameraZoomLevel(80.0)
+
+ self._proxy = QScatterDataProxy()
+ self._series = QScatter3DSeries(self._proxy)
+ self._series.setItemLabelFormat("@xTitle: @xLabel @yTitle: @yLabel @zTitle: @zLabel")
+ self._series.setMeshSmooth(self._smooth)
+ self._graph.addSeries(self._series)
+ self._preset = QtGraphs3D.CameraPreset.FrontLow.value
+
+ self._state = InputState.StateNormal
+ self._dragSpeedModifier = float(15)
+
+ self._graph.selectedElementChanged.connect(self.handleElementSelected)
+ self._graph.dragged.connect(self.handleAxisDragging)
+ self._graph.setDragButton(Qt.MouseButton.LeftButton)
+
+ self.addData()
+
+ def addData(self):
+ # Configure the axes according to the data
+ self._graph.axisX().setTitle("X")
+ self._graph.axisY().setTitle("Y")
+ self._graph.axisZ().setTitle("Z")
+
+ dataArray = []
+ limit = int(sqrt(self._itemCount) / 2.0)
+ for i in range(-limit, limit):
+ for j in range(-limit, limit):
+ x = float(i) + 0.5
+ y = cos(degrees(float(i * j) / self._CURVE_DIVIDER))
+ z = float(j) + 0.5
+ dataArray.append(QScatterDataItem(QVector3D(x, y, z)))
+
+ self._graph.seriesList()[0].dataProxy().resetArray(dataArray)
+
+ @Slot(int)
+ def changeStyle(self, style):
+ comboBox = self.sender()
+ if comboBox:
+ self._style = comboBox.itemData(style)
+ if self._graph.seriesList():
+ self._graph.seriesList()[0].setMesh(self._style)
+
+ @Slot(int)
+ def setSmoothDots(self, smooth):
+ self._smooth = smooth == Qt.CheckState.Checked
+ series = self._graph.seriesList()[0]
+ series.setMeshSmooth(self._smooth)
+
+ @Slot(int)
+ def changeTheme(self, theme):
+ currentTheme = self._graph.activeTheme()
+ currentTheme.setTheme(QGraphsTheme.Theme(theme))
+ self.backgroundEnabledChanged.emit(currentTheme.isPlotAreaBackgroundVisible())
+ self.gridVisibleChanged.emit(currentTheme.isGridVisible())
+
+ @Slot()
+ def changePresetCamera(self):
+ self._graph.setCameraPreset(QtGraphs3D.CameraPreset(self._preset))
+
+ self._preset += 1
+ if self._preset > QtGraphs3D.CameraPreset.DirectlyBelow.value:
+ self._preset = QtGraphs3D.CameraPreset.FrontLow.value
+
+ @Slot(QtGraphs3D.ShadowQuality)
+ def shadowQualityUpdatedByVisual(self, sq):
+ self.shadowQualityChanged.emit(sq.value)
+
+ @Slot(int)
+ def changeShadowQuality(self, quality):
+ sq = QtGraphs3D.ShadowQuality(quality)
+ self._graph.setShadowQuality(sq)
+
+ @Slot(int)
+ def setPlotAreaBackgroundVisible(self, state):
+ enabled = state == Qt.CheckState.Checked
+ self._graph.activeTheme().setPlotAreaBackgroundVisible(enabled)
+
+ @Slot(int)
+ def setGridVisible(self, state):
+ self._graph.activeTheme().setGridVisible(state == Qt.Checked.value)
+
+ @Slot()
+ def toggleItemCount(self):
+ if self._itemCount == NUMBER_OF_ITEMS:
+ self._itemCount = LOWER_NUMBER_OF_ITEMS
+ self._CURVE_DIVIDER = LOWER_CURVE_DIVIDER
+ else:
+ self._itemCount = NUMBER_OF_ITEMS
+ self._CURVE_DIVIDER = CURVE_DIVIDER
+
+ self._graph.seriesList()[0].dataProxy().resetArray([])
+ self.addData()
+
+ @Slot()
+ def toggleRanges(self):
+ if not self._autoAdjust:
+ self._graph.axisX().setAutoAdjustRange(True)
+ self._graph.axisZ().setAutoAdjustRange(True)
+ self._dragSpeedModifier = 1.5
+ self._autoAdjust = True
+ else:
+ self._graph.axisX().setRange(-10.0, 10.0)
+ self._graph.axisZ().setRange(-10.0, 10.0)
+ self._dragSpeedModifier = float(15)
+ self._autoAdjust = False
+
+ @Slot(QtGraphs3D.ElementType)
+ def handleElementSelected(self, type):
+ if type == QtGraphs3D.ElementType.AxisXLabel:
+ self._state = InputState.StateDraggingX
+ elif type == QtGraphs3D.ElementType.AxisYLabel:
+ self._state = InputState.StateDraggingY
+ elif type == QtGraphs3D.ElementType.AxisZLabel:
+ self._state = InputState.StateDraggingZ
+ else:
+ self._state = InputState.StateNormal
+
+ @Slot(QVector2D)
+ def handleAxisDragging(self, delta):
+ distance = 0.0
+ # Get scene orientation from active camera
+ xRotation = self._graph.cameraXRotation()
+ yRotation = self._graph.cameraYRotation()
+
+ # Calculate directional drag multipliers based on rotation
+ xMulX = cos(degrees(xRotation))
+ xMulY = sin(degrees(xRotation))
+ zMulX = sin(degrees(xRotation))
+ zMulY = cos(degrees(xRotation))
+
+ # Get the drag amount
+ move = delta.toPoint()
+
+ # Flip the effect of y movement if we're viewing from below
+ yMove = -move.y() if yRotation < 0 else move.y()
+
+ # Adjust axes
+ if self._state == InputState.StateDraggingX:
+ axis = self._graph.axisX()
+ distance = (move.x() * xMulX - yMove * xMulY) / self._dragSpeedModifier
+ axis.setRange(axis.min() - distance, axis.max() - distance)
+ elif self._state == InputState.StateDraggingZ:
+ axis = self._graph.axisZ()
+ distance = (move.x() * zMulX + yMove * zMulY) / self._dragSpeedModifier
+ axis.setRange(axis.min() + distance, axis.max() + distance)
+ elif self._state == InputState.StateDraggingY:
+ axis = self._graph.axisY()
+ # No need to use adjusted y move here
+ distance = move.y() / self._dragSpeedModifier
+ axis.setRange(axis.min() + distance, axis.max() + distance)
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from PySide6.QtCore import QObject, QSize, Qt
+from PySide6.QtWidgets import (QCheckBox, QComboBox, QCommandLinkButton,
+ QLabel, QHBoxLayout, QSizePolicy,
+ QVBoxLayout, QWidget, )
+from PySide6.QtQuickWidgets import QQuickWidget
+from PySide6.QtGraphs import QAbstract3DSeries
+from PySide6.QtGraphsWidgets import Q3DScatterWidgetItem
+
+from scatterdatamodifier import ScatterDataModifier
+
+
+class ScatterGraph(QObject):
+
+ def __init__(self, minimum_graph_size, maximum_graph_size):
+ super().__init__()
+
+ scatterGraph = Q3DScatterWidgetItem()
+ scatterGraphWidget = QQuickWidget()
+ scatterGraph.setWidget(scatterGraphWidget)
+ self._scatterWidget = QWidget()
+ hLayout = QHBoxLayout(self._scatterWidget)
+ scatterGraphWidget.setMinimumSize(minimum_graph_size)
+ scatterGraphWidget.setMaximumSize(maximum_graph_size)
+ scatterGraphWidget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
+ scatterGraphWidget.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
+ hLayout.addWidget(scatterGraphWidget, 1)
+
+ vLayout = QVBoxLayout()
+ hLayout.addLayout(vLayout)
+
+ cameraButton = QCommandLinkButton(self._scatterWidget)
+ cameraButton.setText("Change camera preset")
+ cameraButton.setDescription("Switch between a number of preset camera positions")
+ cameraButton.setIconSize(QSize(0, 0))
+
+ itemCountButton = QCommandLinkButton(self._scatterWidget)
+ itemCountButton.setText("Toggle item count")
+ itemCountButton.setDescription("Switch between 900 and 10000 data points")
+ itemCountButton.setIconSize(QSize(0, 0))
+
+ rangeButton = QCommandLinkButton(self._scatterWidget)
+ rangeButton.setText("Toggle axis ranges")
+ rangeButton.setDescription("Switch between automatic axis ranges and preset ranges")
+ rangeButton.setIconSize(QSize(0, 0))
+
+ backgroundCheckBox = QCheckBox(self._scatterWidget)
+ backgroundCheckBox.setText("Show graph background")
+ backgroundCheckBox.setChecked(True)
+
+ gridCheckBox = QCheckBox(self._scatterWidget)
+ gridCheckBox.setText("Show grid")
+ gridCheckBox.setChecked(True)
+
+ smoothCheckBox = QCheckBox(self._scatterWidget)
+ smoothCheckBox.setText("Smooth dots")
+ smoothCheckBox.setChecked(True)
+
+ itemStyleList = QComboBox(self._scatterWidget)
+ itemStyleList.addItem("Sphere", QAbstract3DSeries.Mesh.Sphere)
+ itemStyleList.addItem("Cube", QAbstract3DSeries.Mesh.Cube)
+ itemStyleList.addItem("Minimal", QAbstract3DSeries.Mesh.Minimal)
+ itemStyleList.addItem("Point", QAbstract3DSeries.Mesh.Point)
+ itemStyleList.setCurrentIndex(0)
+
+ themeList = QComboBox(self._scatterWidget)
+ themeList.addItem("Qt")
+ themeList.addItem("Primary Colors")
+ themeList.addItem("Digia")
+ themeList.addItem("Stone Moss")
+ themeList.addItem("Army Blue")
+ themeList.addItem("Retro")
+ themeList.addItem("Ebony")
+ themeList.addItem("Isabelle")
+ themeList.setCurrentIndex(3)
+
+ shadowQuality = QComboBox(self._scatterWidget)
+ shadowQuality.addItem("None")
+ shadowQuality.addItem("Low")
+ shadowQuality.addItem("Medium")
+ shadowQuality.addItem("High")
+ shadowQuality.addItem("Low Soft")
+ shadowQuality.addItem("Medium Soft")
+ shadowQuality.addItem("High Soft")
+ shadowQuality.setCurrentIndex(6)
+
+ vLayout.addWidget(cameraButton)
+ vLayout.addWidget(itemCountButton)
+ vLayout.addWidget(rangeButton)
+ vLayout.addWidget(backgroundCheckBox)
+ vLayout.addWidget(gridCheckBox)
+ vLayout.addWidget(smoothCheckBox)
+ vLayout.addWidget(QLabel("Change dot style"))
+ vLayout.addWidget(itemStyleList)
+ vLayout.addWidget(QLabel("Change theme"))
+ vLayout.addWidget(themeList)
+ vLayout.addWidget(QLabel("Adjust shadow quality"))
+ vLayout.addWidget(shadowQuality, 1, Qt.AlignmentFlag.AlignTop)
+
+ modifier = ScatterDataModifier(scatterGraph, self)
+
+ cameraButton.clicked.connect(modifier.changePresetCamera)
+ itemCountButton.clicked.connect(modifier.toggleItemCount)
+ rangeButton.clicked.connect(modifier.toggleRanges)
+
+ backgroundCheckBox.checkStateChanged.connect(modifier.setPlotAreaBackgroundVisible)
+ gridCheckBox.checkStateChanged.connect(modifier.setGridVisible)
+ smoothCheckBox.checkStateChanged.connect(modifier.setSmoothDots)
+
+ modifier.backgroundEnabledChanged.connect(backgroundCheckBox.setChecked)
+ modifier.gridVisibleChanged.connect(gridCheckBox.setChecked)
+ itemStyleList.currentIndexChanged.connect(modifier.changeStyle)
+
+ themeList.currentIndexChanged.connect(modifier.changeTheme)
+
+ shadowQuality.currentIndexChanged.connect(modifier.changeShadowQuality)
+
+ modifier.shadowQualityChanged.connect(shadowQuality.setCurrentIndex)
+ scatterGraph.shadowQualityChanged.connect(modifier.shadowQualityUpdatedByVisual)
+
+ def scatterWidget(self):
+ return self._scatterWidget
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from surfacegraphmodifier import SurfaceGraphModifier
+
+from PySide6.QtCore import QObject, Qt
+from PySide6.QtGui import QBrush, QIcon, QLinearGradient, QPainter, QPixmap
+from PySide6.QtWidgets import (QGroupBox, QCheckBox, QLabel, QHBoxLayout,
+ QPushButton, QRadioButton, QSizePolicy, QSlider,
+ QVBoxLayout, QWidget)
+from PySide6.QtQuickWidgets import QQuickWidget
+from PySide6.QtGraphsWidgets import Q3DSurfaceWidgetItem
+
+
+def gradientBtoYPB_Pixmap():
+ grBtoY = QLinearGradient(0, 0, 1, 100)
+ grBtoY.setColorAt(1.0, Qt.GlobalColor.black)
+ grBtoY.setColorAt(0.67, Qt.GlobalColor.blue)
+ grBtoY.setColorAt(0.33, Qt.GlobalColor.red)
+ grBtoY.setColorAt(0.0, Qt.GlobalColor.yellow)
+ pm = QPixmap(24, 100)
+ with QPainter(pm) as pmp:
+ pmp.setBrush(QBrush(grBtoY))
+ pmp.setPen(Qt.PenStyle.NoPen)
+ pmp.drawRect(0, 0, 24, 100)
+ return pm
+
+
+def gradientGtoRPB_Pixmap():
+ grGtoR = QLinearGradient(0, 0, 1, 100)
+ grGtoR.setColorAt(1.0, Qt.GlobalColor.darkGreen)
+ grGtoR.setColorAt(0.5, Qt.GlobalColor.yellow)
+ grGtoR.setColorAt(0.2, Qt.GlobalColor.red)
+ grGtoR.setColorAt(0.0, Qt.GlobalColor.darkRed)
+ pm = QPixmap(24, 100)
+ with QPainter(pm) as pmp:
+ pmp.setBrush(QBrush(grGtoR))
+ pmp.setPen(Qt.PenStyle.NoPen)
+ pmp.drawRect(0, 0, 24, 100)
+ return pm
+
+
+def highlightPixmap():
+ HEIGHT = 400
+ WIDTH = 110
+ BORDER = 10
+ gr = QLinearGradient(0, 0, 1, HEIGHT - 2 * BORDER)
+ gr.setColorAt(1.0, Qt.GlobalColor.black)
+ gr.setColorAt(0.8, Qt.GlobalColor.darkGreen)
+ gr.setColorAt(0.6, Qt.GlobalColor.green)
+ gr.setColorAt(0.4, Qt.GlobalColor.yellow)
+ gr.setColorAt(0.2, Qt.GlobalColor.red)
+ gr.setColorAt(0.0, Qt.GlobalColor.darkRed)
+ pmHighlight = QPixmap(WIDTH, HEIGHT)
+ pmHighlight.fill(Qt.GlobalColor.transparent)
+ with QPainter(pmHighlight) as pmpHighlight:
+ pmpHighlight.setBrush(QBrush(gr))
+ pmpHighlight.setPen(Qt.PenStyle.NoPen)
+ pmpHighlight.drawRect(BORDER, BORDER, 35, HEIGHT - 2 * BORDER)
+ pmpHighlight.setPen(Qt.GlobalColor.black)
+ step = (HEIGHT - 2 * BORDER) / 5
+ for i in range(0, 6):
+ yPos = i * step + BORDER
+ pmpHighlight.drawLine(BORDER, yPos, 55, yPos)
+ HEIGHT = 550 - (i * 110)
+ pmpHighlight.drawText(60, yPos + 2, f"{HEIGHT} m")
+ return pmHighlight
+
+
+class SurfaceGraph(QObject):
+
+ def __init__(self, minimum_graph_size, maximum_graph_size):
+ super().__init__()
+
+ surfaceGraphWidget = QQuickWidget()
+ surfaceGraph = Q3DSurfaceWidgetItem()
+ surfaceGraph.setWidget(surfaceGraphWidget)
+ self._surfaceWidget = QWidget()
+ hLayout = QHBoxLayout(self._surfaceWidget)
+ surfaceGraphWidget.setMinimumSize(minimum_graph_size)
+ surfaceGraphWidget.setMaximumSize(maximum_graph_size)
+ surfaceGraphWidget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
+ surfaceGraphWidget.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
+ surfaceGraphWidget.setResizeMode(QQuickWidget.ResizeMode.SizeRootObjectToView)
+ hLayout.addWidget(surfaceGraphWidget, 1)
+ vLayout = QVBoxLayout()
+ hLayout.addLayout(vLayout)
+ vLayout.setAlignment(Qt.AlignmentFlag.AlignTop)
+ # Create control widgets
+ modelGroupBox = QGroupBox("Model")
+ sqrtSinModelRB = QRadioButton(self._surfaceWidget)
+ sqrtSinModelRB.setText("Sqrt and Sin")
+ sqrtSinModelRB.setChecked(False)
+ heightMapModelRB = QRadioButton(self._surfaceWidget)
+ heightMapModelRB.setText("Multiseries\nHeight Map")
+ heightMapModelRB.setChecked(False)
+ texturedModelRB = QRadioButton(self._surfaceWidget)
+ texturedModelRB.setText("Textured\nTopography")
+ texturedModelRB.setChecked(False)
+ modelVBox = QVBoxLayout()
+ modelVBox.addWidget(sqrtSinModelRB)
+ modelVBox.addWidget(heightMapModelRB)
+ modelVBox.addWidget(texturedModelRB)
+ modelGroupBox.setLayout(modelVBox)
+ selectionGroupBox = QGroupBox("Graph Selection Mode")
+ modeNoneRB = QRadioButton(self._surfaceWidget)
+ modeNoneRB.setText("No selection")
+ modeNoneRB.setChecked(False)
+ modeItemRB = QRadioButton(self._surfaceWidget)
+ modeItemRB.setText("Item")
+ modeItemRB.setChecked(False)
+ modeSliceRowRB = QRadioButton(self._surfaceWidget)
+ modeSliceRowRB.setText("Row Slice")
+ modeSliceRowRB.setChecked(False)
+ modeSliceColumnRB = QRadioButton(self._surfaceWidget)
+ modeSliceColumnRB.setText("Column Slice")
+ modeSliceColumnRB.setChecked(False)
+ selectionVBox = QVBoxLayout()
+ selectionVBox.addWidget(modeNoneRB)
+ selectionVBox.addWidget(modeItemRB)
+ selectionVBox.addWidget(modeSliceRowRB)
+ selectionVBox.addWidget(modeSliceColumnRB)
+ selectionGroupBox.setLayout(selectionVBox)
+ axisGroupBox = QGroupBox("Axis ranges")
+ axisMinSliderX = QSlider(Qt.Orientation.Horizontal)
+ axisMinSliderX.setMinimum(0)
+ axisMinSliderX.setTickInterval(1)
+ axisMinSliderX.setEnabled(True)
+ axisMaxSliderX = QSlider(Qt.Orientation.Horizontal)
+ axisMaxSliderX.setMinimum(1)
+ axisMaxSliderX.setTickInterval(1)
+ axisMaxSliderX.setEnabled(True)
+ axisMinSliderZ = QSlider(Qt.Orientation.Horizontal)
+ axisMinSliderZ.setMinimum(0)
+ axisMinSliderZ.setTickInterval(1)
+ axisMinSliderZ.setEnabled(True)
+ axisMaxSliderZ = QSlider(Qt.Orientation.Horizontal)
+ axisMaxSliderZ.setMinimum(1)
+ axisMaxSliderZ.setTickInterval(1)
+ axisMaxSliderZ.setEnabled(True)
+ axisVBox = QVBoxLayout(axisGroupBox)
+ axisVBox.addWidget(QLabel("Column range"))
+ axisVBox.addWidget(axisMinSliderX)
+ axisVBox.addWidget(axisMaxSliderX)
+ axisVBox.addWidget(QLabel("Row range"))
+ axisVBox.addWidget(axisMinSliderZ)
+ axisVBox.addWidget(axisMaxSliderZ)
+ # Mode-dependent controls
+ # sqrt-sin
+ colorGroupBox = QGroupBox("Custom gradient")
+
+ pixmap = gradientBtoYPB_Pixmap()
+ gradientBtoYPB = QPushButton(self._surfaceWidget)
+ gradientBtoYPB.setIcon(QIcon(pixmap))
+ gradientBtoYPB.setIconSize(pixmap.size())
+
+ pixmap = gradientGtoRPB_Pixmap()
+ gradientGtoRPB = QPushButton(self._surfaceWidget)
+ gradientGtoRPB.setIcon(QIcon(pixmap))
+ gradientGtoRPB.setIconSize(pixmap.size())
+
+ colorHBox = QHBoxLayout(colorGroupBox)
+ colorHBox.addWidget(gradientBtoYPB)
+ colorHBox.addWidget(gradientGtoRPB)
+ # Multiseries heightmap
+ showGroupBox = QGroupBox("Show Object")
+ showGroupBox.setVisible(False)
+ checkboxShowOilRigOne = QCheckBox("Oil Rig 1")
+ checkboxShowOilRigOne.setChecked(True)
+ checkboxShowOilRigTwo = QCheckBox("Oil Rig 2")
+ checkboxShowOilRigTwo.setChecked(True)
+ checkboxShowRefinery = QCheckBox("Refinery")
+ showVBox = QVBoxLayout()
+ showVBox.addWidget(checkboxShowOilRigOne)
+ showVBox.addWidget(checkboxShowOilRigTwo)
+ showVBox.addWidget(checkboxShowRefinery)
+ showGroupBox.setLayout(showVBox)
+ visualsGroupBox = QGroupBox("Visuals")
+ visualsGroupBox.setVisible(False)
+ checkboxVisualsSeeThrough = QCheckBox("See-Through")
+ checkboxHighlightOil = QCheckBox("Highlight Oil")
+ checkboxShowShadows = QCheckBox("Shadows")
+ checkboxShowShadows.setChecked(True)
+ visualVBox = QVBoxLayout(visualsGroupBox)
+ visualVBox.addWidget(checkboxVisualsSeeThrough)
+ visualVBox.addWidget(checkboxHighlightOil)
+ visualVBox.addWidget(checkboxShowShadows)
+ labelSelection = QLabel("Selection:")
+ labelSelection.setVisible(False)
+ labelSelectedItem = QLabel("Nothing")
+ labelSelectedItem.setVisible(False)
+ # Textured topography heightmap
+ enableTexture = QCheckBox("Surface texture")
+ enableTexture.setVisible(False)
+
+ label = QLabel(self._surfaceWidget)
+ label.setPixmap(highlightPixmap())
+ heightMapGroupBox = QGroupBox("Highlight color map")
+ colorMapVBox = QVBoxLayout()
+ colorMapVBox.addWidget(label)
+ heightMapGroupBox.setLayout(colorMapVBox)
+ heightMapGroupBox.setVisible(False)
+ # Populate vertical layout
+ # Common
+ vLayout.addWidget(modelGroupBox)
+ vLayout.addWidget(selectionGroupBox)
+ vLayout.addWidget(axisGroupBox)
+ # Sqrt Sin
+ vLayout.addWidget(colorGroupBox)
+ # Multiseries heightmap
+ vLayout.addWidget(showGroupBox)
+ vLayout.addWidget(visualsGroupBox)
+ vLayout.addWidget(labelSelection)
+ vLayout.addWidget(labelSelectedItem)
+ # Textured topography
+ vLayout.addWidget(heightMapGroupBox)
+ vLayout.addWidget(enableTexture)
+ # Create the controller
+ modifier = SurfaceGraphModifier(surfaceGraph, labelSelectedItem, self)
+ # Connect widget controls to controller
+ heightMapModelRB.toggled.connect(modifier.enableHeightMapModel)
+ sqrtSinModelRB.toggled.connect(modifier.enableSqrtSinModel)
+ texturedModelRB.toggled.connect(modifier.enableTopographyModel)
+ modeNoneRB.toggled.connect(modifier.toggleModeNone)
+ modeItemRB.toggled.connect(modifier.toggleModeItem)
+ modeSliceRowRB.toggled.connect(modifier.toggleModeSliceRow)
+ modeSliceColumnRB.toggled.connect(modifier.toggleModeSliceColumn)
+ axisMinSliderX.valueChanged.connect(modifier.adjustXMin)
+ axisMaxSliderX.valueChanged.connect(modifier.adjustXMax)
+ axisMinSliderZ.valueChanged.connect(modifier.adjustZMin)
+ axisMaxSliderZ.valueChanged.connect(modifier.adjustZMax)
+ # Mode dependent connections
+ gradientBtoYPB.pressed.connect(modifier.setBlackToYellowGradient)
+ gradientGtoRPB.pressed.connect(modifier.setGreenToRedGradient)
+ checkboxShowOilRigOne.toggled.connect(modifier.toggleItemOne)
+ checkboxShowOilRigTwo.toggled.connect(modifier.toggleItemTwo)
+ checkboxShowRefinery.toggled.connect(modifier.toggleItemThree)
+ checkboxVisualsSeeThrough.toggled.connect(modifier.toggleSeeThrough)
+ checkboxHighlightOil.toggled.connect(modifier.toggleOilHighlight)
+ checkboxShowShadows.toggled.connect(modifier.toggleShadows)
+ enableTexture.toggled.connect(modifier.toggleSurfaceTexture)
+ # Connections to disable features depending on mode
+ sqrtSinModelRB.toggled.connect(colorGroupBox.setVisible)
+ heightMapModelRB.toggled.connect(showGroupBox.setVisible)
+ heightMapModelRB.toggled.connect(visualsGroupBox.setVisible)
+ heightMapModelRB.toggled.connect(labelSelection.setVisible)
+ heightMapModelRB.toggled.connect(labelSelectedItem.setVisible)
+ texturedModelRB.toggled.connect(enableTexture.setVisible)
+ texturedModelRB.toggled.connect(heightMapGroupBox.setVisible)
+ modifier.setAxisMinSliderX(axisMinSliderX)
+ modifier.setAxisMaxSliderX(axisMaxSliderX)
+ modifier.setAxisMinSliderZ(axisMinSliderZ)
+ modifier.setAxisMaxSliderZ(axisMaxSliderZ)
+ sqrtSinModelRB.setChecked(True)
+ modeItemRB.setChecked(True)
+ enableTexture.setChecked(True)
+
+ def surfaceWidget(self):
+ return self._surfaceWidget
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+import os
+from enum import Enum
+from math import sqrt, sin, cos, degrees
+from pathlib import Path
+
+from PySide6.QtCore import QObject, QPropertyAnimation, Qt, Slot
+from PySide6.QtGui import (QColor, QFont, QImage, QLinearGradient,
+ QQuaternion, QVector2D, QVector3D, QWheelEvent)
+from PySide6.QtGraphs import (QCustom3DItem,
+ QCustom3DLabel, QHeightMapSurfaceDataProxy,
+ QValue3DAxis, QSurfaceDataItem,
+ QSurfaceDataProxy, QSurface3DSeries,
+ QtGraphs3D, QGraphsTheme)
+
+
+from highlightseries import HighlightSeries
+from topographicseries import TopographicSeries
+
+
+class InputState(Enum):
+ StateNormal = 0
+ StateDraggingX = 1
+ StateDraggingZ = 2
+ StateDraggingY = 3
+
+
+SAMPLE_COUNT_X = 150
+SAMPLE_COUNT_Z = 150
+HEIGHTMAP_GRID_STEP_X = 6
+HEIGHTMAP_GRID_STEP_Z = 6
+SAMPLE_MIN = -8.0
+SAMPLE_MAX = 8.0
+SPEED_MODIFIER = 20.0
+
+AREA_WIDTH = 8000.0
+AREA_HEIGHT = 8000.0
+ASPECT_RATIO = 0.1389
+MIN_RANGE = AREA_WIDTH * 0.49
+
+
+class SurfaceGraphModifier(QObject):
+
+ def __init__(self, surface, label, parent):
+ super().__init__(parent)
+ self._state = InputState.StateNormal
+ self._data_path = Path(__file__).resolve().parent / "data"
+ self._graph = surface
+ self._textField = label
+ self._sqrtSinProxy = None
+ self._sqrtSinSeries = None
+ self._heightMapProxyOne = None
+ self._heightMapProxyTwo = None
+ self._heightMapProxyThree = None
+ self._heightMapSeriesOne = None
+ self._heightMapSeriesTwo = None
+ self._heightMapSeriesThree = None
+
+ self._axisMinSliderX = None
+ self._axisMaxSliderX = None
+ self._axisMinSliderZ = None
+ self._axisMaxSliderZ = None
+ self._rangeMinX = 0.0
+ self._rangeMinZ = 0.0
+ self._stepX = 0.0
+ self._stepZ = 0.0
+ self._heightMapWidth = 0
+ self._heightMapHeight = 0
+
+ self._axisXMinValue = 0.0
+ self._axisXMaxValue = 0.0
+ self._axisXMinRange = 0.0
+ self._axisZMinValue = 0.0
+ self._axisZMaxValue = 0.0
+ self._axisZMinRange = 0.0
+ self._areaMinValue = 0.0
+ self._areaMaxValue = 0.0
+
+ self._selectionAnimation = None
+ self._titleLabel = None
+ self._previouslyAnimatedItem = None
+ self._previousScaling = {}
+
+ self._topography = None
+ self._highlight = None
+ self._highlightWidth = 0
+ self._highlightHeight = 0
+
+ self._graph.setCameraZoomLevel(85.0)
+ self._graph.setCameraPreset(QtGraphs3D.CameraPreset.IsometricRight)
+ theme = self._graph.activeTheme()
+ theme.setTheme(QGraphsTheme.Theme.MixSeries)
+ theme.setLabelBackgroundVisible(False)
+ theme.setLabelBorderVisible(False)
+
+ self._x_axis = QValue3DAxis()
+ self._y_axis = QValue3DAxis()
+ self._z_axis = QValue3DAxis()
+ self._graph.setAxisX(self._x_axis)
+ self._graph.setAxisY(self._y_axis)
+ self._graph.setAxisZ(self._z_axis)
+
+ #
+ # Sqrt Sin
+ #
+ self._sqrtSinProxy = QSurfaceDataProxy()
+ self._sqrtSinSeries = QSurface3DSeries(self._sqrtSinProxy)
+ self.fillSqrtSinProxy()
+
+ #
+ # Multisurface heightmap
+ #
+ # Create the first surface layer
+ heightMapImageOne = QImage(self._data_path / "layer_1.png")
+ self._heightMapProxyOne = QHeightMapSurfaceDataProxy(heightMapImageOne)
+ self._heightMapSeriesOne = QSurface3DSeries(self._heightMapProxyOne)
+ self._heightMapSeriesOne.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
+ self._heightMapProxyOne.setValueRanges(34.0, 40.0, 18.0, 24.0)
+
+ # Create the other 2 surface layers
+ heightMapImageTwo = QImage(self._data_path / "layer_2.png")
+ self._heightMapProxyTwo = QHeightMapSurfaceDataProxy(heightMapImageTwo)
+ self._heightMapSeriesTwo = QSurface3DSeries(self._heightMapProxyTwo)
+ self._heightMapSeriesTwo.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
+ self._heightMapProxyTwo.setValueRanges(34.0, 40.0, 18.0, 24.0)
+
+ heightMapImageThree = QImage(self._data_path / "layer_3.png")
+ self._heightMapProxyThree = QHeightMapSurfaceDataProxy(heightMapImageThree)
+ self._heightMapSeriesThree = QSurface3DSeries(self._heightMapProxyThree)
+ self._heightMapSeriesThree.setItemLabelFormat("(@xLabel, @zLabel): @yLabel")
+ self._heightMapProxyThree.setValueRanges(34.0, 40.0, 18.0, 24.0)
+
+ # The images are the same size, so it's enough to get the dimensions
+ # from one
+ self._heightMapWidth = heightMapImageOne.width()
+ self._heightMapHeight = heightMapImageOne.height()
+
+ # Set the gradients for multi-surface layers
+ grOne = QLinearGradient()
+ grOne.setColorAt(0.0, Qt.GlobalColor.black)
+ grOne.setColorAt(0.38, Qt.GlobalColor.darkYellow)
+ grOne.setColorAt(0.39, Qt.GlobalColor.darkGreen)
+ grOne.setColorAt(0.5, Qt.GlobalColor.darkGray)
+ grOne.setColorAt(1.0, Qt.GlobalColor.gray)
+ self._heightMapSeriesOne.setBaseGradient(grOne)
+ self._heightMapSeriesOne.setColorStyle(QGraphsTheme.ColorStyle.RangeGradient)
+
+ grTwo = QLinearGradient()
+ grTwo.setColorAt(0.39, Qt.GlobalColor.blue)
+ grTwo.setColorAt(0.4, Qt.GlobalColor.white)
+ self._heightMapSeriesTwo.setBaseGradient(grTwo)
+ self._heightMapSeriesTwo.setColorStyle(QGraphsTheme.ColorStyle.RangeGradient)
+
+ grThree = QLinearGradient()
+ grThree.setColorAt(0.0, Qt.GlobalColor.white)
+ grThree.setColorAt(0.05, Qt.GlobalColor.black)
+ self._heightMapSeriesThree.setBaseGradient(grThree)
+ self._heightMapSeriesThree.setColorStyle(QGraphsTheme.ColorStyle.RangeGradient)
+
+ # Custom items and label
+ self._graph.selectedElementChanged.connect(self.handleElementSelected)
+
+ self._selectionAnimation = QPropertyAnimation(self)
+ self._selectionAnimation.setPropertyName(b"scaling")
+ self._selectionAnimation.setDuration(500)
+ self._selectionAnimation.setLoopCount(-1)
+
+ titleFont = QFont("Century Gothic", 30)
+ titleFont.setBold(True)
+ self._titleLabel = QCustom3DLabel("Oil Rigs on Imaginary Sea", titleFont,
+ QVector3D(0.0, 1.2, 0.0),
+ QVector3D(1.0, 1.0, 0.0),
+ QQuaternion())
+ self._titleLabel.setPositionAbsolute(True)
+ self._titleLabel.setFacingCamera(True)
+ self._titleLabel.setBackgroundColor(QColor(0x66cdaa))
+ self._graph.addCustomItem(self._titleLabel)
+ self._titleLabel.setVisible(False)
+
+ # Make two of the custom object visible
+ self.toggleItemOne(True)
+ self.toggleItemTwo(True)
+
+ #
+ # Topographic map
+ #
+ self._topography = TopographicSeries()
+ file_name = os.fspath(self._data_path / "topography.png")
+ self._topography.setTopographyFile(file_name, AREA_WIDTH, AREA_HEIGHT)
+ self._topography.setItemLabelFormat("@yLabel m")
+
+ self._highlight = HighlightSeries()
+ self._highlight.setTopographicSeries(self._topography)
+ self._highlight.setMinHeight(MIN_RANGE * ASPECT_RATIO)
+ self._highlight.handleGradientChange(AREA_WIDTH * ASPECT_RATIO)
+ self._graph.axisY().maxChanged.connect(self._highlight.handleGradientChange)
+
+ self._graph.wheel.connect(self.onWheel)
+ self._graph.dragged.connect(self.handleAxisDragging)
+
+ def fillSqrtSinProxy(self):
+ stepX = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_X - 1)
+ stepZ = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_Z - 1)
+
+ dataArray = []
+ for i in range(0, SAMPLE_COUNT_Z):
+ newRow = []
+ # Keep values within range bounds, since just adding step can
+ # cause minor drift due to the rounding errors.
+ z = min(SAMPLE_MAX, (i * stepZ + SAMPLE_MIN))
+ for j in range(0, SAMPLE_COUNT_X):
+ x = min(SAMPLE_MAX, (j * stepX + SAMPLE_MIN))
+ R = sqrt(z * z + x * x) + 0.01
+ y = (sin(R) / R + 0.24) * 1.61
+ item = QSurfaceDataItem(QVector3D(x, y, z))
+ newRow.append(item)
+ dataArray.append(newRow)
+ self._sqrtSinProxy.resetArray(dataArray)
+
+ @Slot(bool)
+ def enableSqrtSinModel(self, enable):
+ if enable:
+ self._sqrtSinSeries.setDrawMode(QSurface3DSeries.DrawFlag.DrawSurfaceAndWireframe)
+ self._sqrtSinSeries.setShading(QSurface3DSeries.Shading.Flat)
+
+ self._graph.axisX().setLabelFormat("%.2f")
+ self._graph.axisZ().setLabelFormat("%.2f")
+ self._graph.axisX().setRange(SAMPLE_MIN, SAMPLE_MAX)
+ self._graph.axisY().setRange(0.0, 2.0)
+ self._graph.axisZ().setRange(SAMPLE_MIN, SAMPLE_MAX)
+ self._graph.axisX().setLabelAutoAngle(30.0)
+ self._graph.axisY().setLabelAutoAngle(90.0)
+ self._graph.axisZ().setLabelAutoAngle(30.0)
+
+ self._graph.removeSeries(self._heightMapSeriesOne)
+ self._graph.removeSeries(self._heightMapSeriesTwo)
+ self._graph.removeSeries(self._heightMapSeriesThree)
+ self._graph.removeSeries(self._topography)
+ self._graph.removeSeries(self._highlight)
+
+ self._graph.addSeries(self._sqrtSinSeries)
+
+ self._titleLabel.setVisible(False)
+ self._graph.axisX().setTitleVisible(False)
+ self._graph.axisY().setTitleVisible(False)
+ self._graph.axisZ().setTitleVisible(False)
+
+ self._graph.axisX().setTitle("")
+ self._graph.axisY().setTitle("")
+ self._graph.axisZ().setTitle("")
+
+ # Reset range sliders for Sqrt & Sin
+ self._rangeMinX = SAMPLE_MIN
+ self._rangeMinZ = SAMPLE_MIN
+ self._stepX = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_X - 1)
+ self._stepZ = (SAMPLE_MAX - SAMPLE_MIN) / float(SAMPLE_COUNT_Z - 1)
+ self._axisMinSliderX.setMinimum(0)
+ self._axisMinSliderX.setMaximum(SAMPLE_COUNT_X - 2)
+ self._axisMinSliderX.setValue(0)
+ self._axisMaxSliderX.setMinimum(1)
+ self._axisMaxSliderX.setMaximum(SAMPLE_COUNT_X - 1)
+ self._axisMaxSliderX.setValue(SAMPLE_COUNT_X - 1)
+ self._axisMinSliderZ.setMinimum(0)
+ self._axisMinSliderZ.setMaximum(SAMPLE_COUNT_Z - 2)
+ self._axisMinSliderZ.setValue(0)
+ self._axisMaxSliderZ.setMinimum(1)
+ self._axisMaxSliderZ.setMaximum(SAMPLE_COUNT_Z - 1)
+ self._axisMaxSliderZ.setValue(SAMPLE_COUNT_Z - 1)
+
+ self._graph.setZoomEnabled(True)
+
+ @Slot(bool)
+ def enableHeightMapModel(self, enable):
+ if enable:
+ self._heightMapSeriesOne.setDrawMode(QSurface3DSeries.DrawFlag.DrawSurface)
+ self._heightMapSeriesOne.setShading(QSurface3DSeries.Shading.Flat)
+ self._heightMapSeriesTwo.setDrawMode(QSurface3DSeries.DrawFlag.DrawSurface)
+ self._heightMapSeriesTwo.setShading(QSurface3DSeries.Shading.Flat)
+ self._heightMapSeriesThree.setDrawMode(QSurface3DSeries.DrawFlag.DrawSurface)
+ self._heightMapSeriesThree.setShading(QSurface3DSeries.Shading.Flat)
+
+ self._graph.axisX().setLabelFormat("%.1f N")
+ self._graph.axisZ().setLabelFormat("%.1f E")
+ self._graph.axisX().setRange(34.0, 40.0)
+ self._graph.axisY().setAutoAdjustRange(True)
+ self._graph.axisZ().setRange(18.0, 24.0)
+
+ self._graph.axisX().setTitle("Latitude")
+ self._graph.axisY().setTitle("Height")
+ self._graph.axisZ().setTitle("Longitude")
+
+ self._graph.removeSeries(self._sqrtSinSeries)
+ self._graph.removeSeries(self._topography)
+ self._graph.removeSeries(self._highlight)
+ self._graph.addSeries(self._heightMapSeriesOne)
+ self._graph.addSeries(self._heightMapSeriesTwo)
+ self._graph.addSeries(self._heightMapSeriesThree)
+
+ self._titleLabel.setVisible(True)
+ self._graph.axisX().setTitleVisible(True)
+ self._graph.axisY().setTitleVisible(True)
+ self._graph.axisZ().setTitleVisible(True)
+
+ # Reset range sliders for height map
+ mapGridCountX = self._heightMapWidth / HEIGHTMAP_GRID_STEP_X
+ mapGridCountZ = self._heightMapHeight / HEIGHTMAP_GRID_STEP_Z
+ self._rangeMinX = 34.0
+ self._rangeMinZ = 18.0
+ self._stepX = 6.0 / float(mapGridCountX - 1)
+ self._stepZ = 6.0 / float(mapGridCountZ - 1)
+ self._axisMinSliderX.setMinimum(0)
+ self._axisMinSliderX.setMaximum(mapGridCountX - 2)
+ self._axisMinSliderX.setValue(0)
+ self._axisMaxSliderX.setMinimum(1)
+ self._axisMaxSliderX.setMaximum(mapGridCountX - 1)
+ self._axisMaxSliderX.setValue(mapGridCountX - 1)
+ self._axisMinSliderZ.setMinimum(0)
+ self._axisMinSliderZ.setMaximum(mapGridCountZ - 2)
+ self._axisMinSliderZ.setValue(0)
+ self._axisMaxSliderZ.setMinimum(1)
+ self._axisMaxSliderZ.setMaximum(mapGridCountZ - 1)
+ self._axisMaxSliderZ.setValue(mapGridCountZ - 1)
+
+ self._graph.wheel.disconnect(self.onWheel)
+ self._graph.dragged.disconnect(self.handleAxisDragging)
+ self._graph.setDefaultInputHandler()
+ self._graph.setZoomEnabled(True)
+
+ @Slot(bool)
+ def enableTopographyModel(self, enable):
+ if enable:
+ self._graph.axisX().setLabelFormat("%i")
+ self._graph.axisZ().setLabelFormat("%i")
+ self._graph.axisX().setRange(0.0, AREA_WIDTH)
+ self._graph.axisY().setRange(100.0, AREA_WIDTH * ASPECT_RATIO)
+ self._graph.axisZ().setRange(0.0, AREA_HEIGHT)
+ self._graph.axisX().setLabelAutoAngle(30.0)
+ self._graph.axisY().setLabelAutoAngle(90.0)
+ self._graph.axisZ().setLabelAutoAngle(30.0)
+
+ self._graph.removeSeries(self._heightMapSeriesOne)
+ self._graph.removeSeries(self._heightMapSeriesTwo)
+ self._graph.removeSeries(self._heightMapSeriesThree)
+ self._graph.addSeries(self._topography)
+ self._graph.addSeries(self._highlight)
+
+ self._titleLabel.setVisible(False)
+ self._graph.axisX().setTitleVisible(False)
+ self._graph.axisY().setTitleVisible(False)
+ self._graph.axisZ().setTitleVisible(False)
+
+ self._graph.axisX().setTitle("")
+ self._graph.axisY().setTitle("")
+ self._graph.axisZ().setTitle("")
+
+ # Reset range sliders for topography map
+ self._rangeMinX = 0.0
+ self._rangeMinZ = 0.0
+ self._stepX = 1.0
+ self._stepZ = 1.0
+ self._axisMinSliderX.setMinimum(0)
+ self._axisMinSliderX.setMaximum(AREA_WIDTH - 200)
+ self._axisMinSliderX.setValue(0)
+ self._axisMaxSliderX.setMinimum(200)
+ self._axisMaxSliderX.setMaximum(AREA_WIDTH)
+ self._axisMaxSliderX.setValue(AREA_WIDTH)
+ self._axisMinSliderZ.setMinimum(0)
+ self._axisMinSliderZ.setMaximum(AREA_HEIGHT - 200)
+ self._axisMinSliderZ.setValue(0)
+ self._axisMaxSliderZ.setMinimum(200)
+ self._axisMaxSliderZ.setMaximum(AREA_HEIGHT)
+ self._axisMaxSliderZ.setValue(AREA_HEIGHT)
+
+ self._areaMinValue = 0
+ self._areaMaxValue = AREA_WIDTH
+ self._axisXMinValue = self._areaMinValue
+ self._axisXMaxValue = self._areaMaxValue
+ self._axisZMinValue = self._areaMinValue
+ self._axisZMaxValue = self._areaMaxValue
+ self._axisXMinRange = MIN_RANGE
+ self._axisZMinRange = MIN_RANGE
+
+ self._graph.wheel.connect(self.onWheel)
+ self._graph.dragged.connect(self.handleAxisDragging)
+ self._graph.setZoomEnabled(False)
+
+ def adjustXMin(self, min):
+ minX = self._stepX * float(min) + self._rangeMinX
+
+ max = self._axisMaxSliderX.value()
+ if min >= max:
+ max = min + 1
+ self._axisMaxSliderX.setValue(max)
+
+ maxX = self._stepX * max + self._rangeMinX
+
+ self.setAxisXRange(minX, maxX)
+
+ def adjustXMax(self, max):
+ maxX = self._stepX * float(max) + self._rangeMinX
+
+ min = self._axisMinSliderX.value()
+ if max <= min:
+ min = max - 1
+ self._axisMinSliderX.setValue(min)
+
+ minX = self._stepX * min + self._rangeMinX
+
+ self.setAxisXRange(minX, maxX)
+
+ def adjustZMin(self, min):
+ minZ = self._stepZ * float(min) + self._rangeMinZ
+
+ max = self._axisMaxSliderZ.value()
+ if min >= max:
+ max = min + 1
+ self._axisMaxSliderZ.setValue(max)
+
+ maxZ = self._stepZ * max + self._rangeMinZ
+
+ self.setAxisZRange(minZ, maxZ)
+
+ def adjustZMax(self, max):
+ maxX = self._stepZ * float(max) + self._rangeMinZ
+
+ min = self._axisMinSliderZ.value()
+ if max <= min:
+ min = max - 1
+ self._axisMinSliderZ.setValue(min)
+
+ minX = self._stepZ * min + self._rangeMinZ
+
+ self.setAxisZRange(minX, maxX)
+
+ def setAxisXRange(self, min, max):
+ self._graph.axisX().setRange(min, max)
+
+ def setAxisZRange(self, min, max):
+ self._graph.axisZ().setRange(min, max)
+
+ def setBlackToYellowGradient(self):
+ gr = QLinearGradient()
+ gr.setColorAt(0.0, Qt.GlobalColor.black)
+ gr.setColorAt(0.33, Qt.blue)
+ gr.setColorAt(0.67, Qt.red)
+ gr.setColorAt(1.0, Qt.yellow)
+
+ self._sqrtSinSeries.setBaseGradient(gr)
+ self._sqrtSinSeries.setColorStyle(QGraphsTheme.ColorStyle.RangeGradient)
+
+ def setGreenToRedGradient(self):
+ gr = QLinearGradient()
+ gr.setColorAt(0.0, Qt.darkGreen)
+ gr.setColorAt(0.5, Qt.yellow)
+ gr.setColorAt(0.8, Qt.red)
+ gr.setColorAt(1.0, Qt.darkRed)
+
+ self._sqrtSinSeries.setBaseGradient(gr)
+ self._sqrtSinSeries.setColorStyle(QGraphsTheme.ColorStyle.RangeGradient)
+
+ @Slot(bool)
+ def toggleItemOne(self, show):
+ positionOne = QVector3D(39.0, 77.0, 19.2)
+ positionOnePipe = QVector3D(39.0, 45.0, 19.2)
+ positionOneLabel = QVector3D(39.0, 107.0, 19.2)
+ if show:
+ color = QImage(2, 2, QImage.Format.Format_RGB32)
+ color.fill(Qt.GlobalColor.red)
+ file_name = os.fspath(self._data_path / "oilrig.mesh")
+ item = QCustom3DItem(file_name, positionOne,
+ QVector3D(0.025, 0.025, 0.025),
+ QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 45.0),
+ color)
+ self._graph.addCustomItem(item)
+ file_name = os.fspath(self._data_path / "pipe.mesh")
+ item = QCustom3DItem(file_name, positionOnePipe,
+ QVector3D(0.005, 0.5, 0.005), QQuaternion(),
+ color)
+ item.setShadowCasting(False)
+ self._graph.addCustomItem(item)
+
+ label = QCustom3DLabel()
+ label.setText("Oil Rig One")
+ label.setPosition(positionOneLabel)
+ label.setScaling(QVector3D(1.0, 1.0, 1.0))
+ self._graph.addCustomItem(label)
+ else:
+ self.resetSelection()
+ self._graph.removeCustomItemAt(positionOne)
+ self._graph.removeCustomItemAt(positionOnePipe)
+ self._graph.removeCustomItemAt(positionOneLabel)
+
+ @Slot(bool)
+ def toggleItemTwo(self, show):
+ positionTwo = QVector3D(34.5, 77.0, 23.4)
+ positionTwoPipe = QVector3D(34.5, 45.0, 23.4)
+ positionTwoLabel = QVector3D(34.5, 107.0, 23.4)
+ if show:
+ color = QImage(2, 2, QImage.Format.Format_RGB32)
+ color.fill(Qt.GlobalColor.red)
+ item = QCustom3DItem()
+ file_name = os.fspath(self._data_path / "oilrig.mesh")
+ item.setMeshFile(file_name)
+ item.setPosition(positionTwo)
+ item.setScaling(QVector3D(0.025, 0.025, 0.025))
+ item.setRotation(QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 25.0))
+ item.setTextureImage(color)
+ self._graph.addCustomItem(item)
+ file_name = os.fspath(self._data_path / "pipe.mesh")
+ item = QCustom3DItem(file_name, positionTwoPipe,
+ QVector3D(0.005, 0.5, 0.005), QQuaternion(),
+ color)
+ item.setShadowCasting(False)
+ self._graph.addCustomItem(item)
+
+ label = QCustom3DLabel()
+ label.setText("Oil Rig Two")
+ label.setPosition(positionTwoLabel)
+ label.setScaling(QVector3D(1.0, 1.0, 1.0))
+ self._graph.addCustomItem(label)
+ else:
+ self.resetSelection()
+ self._graph.removeCustomItemAt(positionTwo)
+ self._graph.removeCustomItemAt(positionTwoPipe)
+ self._graph.removeCustomItemAt(positionTwoLabel)
+
+ @Slot(bool)
+ def toggleItemThree(self, show):
+ positionThree = QVector3D(34.5, 86.0, 19.1)
+ positionThreeLabel = QVector3D(34.5, 116.0, 19.1)
+ if show:
+ color = QImage(2, 2, QImage.Format.Format_RGB32)
+ color.fill(Qt.darkMagenta)
+ item = QCustom3DItem()
+ file_name = os.fspath(self._data_path / "refinery.mesh")
+ item.setMeshFile(file_name)
+ item.setPosition(positionThree)
+ item.setScaling(QVector3D(0.04, 0.04, 0.04))
+ item.setRotation(QQuaternion.fromAxisAndAngle(0.0, 1.0, 0.0, 75.0))
+ item.setTextureImage(color)
+ self._graph.addCustomItem(item)
+
+ label = QCustom3DLabel()
+ label.setText("Refinery")
+ label.setPosition(positionThreeLabel)
+ label.setScaling(QVector3D(1.0, 1.0, 1.0))
+ self._graph.addCustomItem(label)
+ else:
+ self.resetSelection()
+ self._graph.removeCustomItemAt(positionThree)
+ self._graph.removeCustomItemAt(positionThreeLabel)
+
+ @Slot(bool)
+ def toggleSeeThrough(self, seethrough):
+ s0 = self._graph.seriesList()[0]
+ s1 = self._graph.seriesList()[1]
+ if seethrough:
+ s0.setDrawMode(QSurface3DSeries.DrawWireframe)
+ s1.setDrawMode(QSurface3DSeries.DrawWireframe)
+ else:
+ s0.setDrawMode(QSurface3DSeries.DrawSurface)
+ s1.setDrawMode(QSurface3DSeries.DrawSurface)
+
+ @Slot(bool)
+ def toggleOilHighlight(self, highlight):
+ s2 = self._graph.seriesList()[2]
+ if highlight:
+ grThree = QLinearGradient()
+ grThree.setColorAt(0.0, Qt.GlobalColor.black)
+ grThree.setColorAt(0.05, Qt.red)
+ s2.setBaseGradient(grThree)
+ else:
+ grThree = QLinearGradient()
+ grThree.setColorAt(0.0, Qt.GlobalColor.white)
+ grThree.setColorAt(0.05, Qt.GlobalColor.black)
+ s2.setBaseGradient(grThree)
+
+ @Slot(bool)
+ def toggleShadows(self, shadows):
+ sq = (QtGraphs3D.ShadowQuality.Medium
+ if shadows else QtGraphs3D.ShadowQuality.None_)
+ self._graph.setShadowQuality(sq)
+
+ @Slot(bool)
+ def toggleSurfaceTexture(self, enable):
+ if enable:
+ file_name = os.fspath(self._data_path / "maptexture.jpg")
+ self._topography.setTextureFile(file_name)
+ else:
+ self._topography.setTextureFile("")
+
+ def handleElementSelected(self, type):
+ self.resetSelection()
+ if type == QtGraphs3D.ElementType.CustomItem:
+ item = self._graph.selectedCustomItem()
+ text = ""
+ if isinstance(item, QCustom3DItem):
+ text += "Custom label: "
+ else:
+ file = item.meshFile().split("/")[-1]
+ text += f"{file}: "
+
+ text += str(self._graph.selectedCustomItemIndex())
+ self._textField.setText(text)
+ self._previouslyAnimatedItem = item
+ self._previousScaling = item.scaling()
+ self._selectionAnimation.setTargetObject(item)
+ self._selectionAnimation.setStartValue(item.scaling())
+ self._selectionAnimation.setEndValue(item.scaling() * 1.5)
+ self._selectionAnimation.start()
+ elif type == QtGraphs3D.ElementType.Series:
+ text = "Surface ("
+ series = self._graph.selectedSeries()
+ if series:
+ point = series.selectedPoint()
+ text += f"{point.x()}, {point.y()}"
+ text += ")"
+ self._textField.setText(text)
+ elif (type.value > QtGraphs3D.ElementType.Series.value
+ and type.value < QtGraphs3D.ElementType.CustomItem.value):
+ index = self._graph.selectedLabelIndex()
+ text = ""
+ if type == QtGraphs3D.ElementType.AxisXLabel:
+ text += "Axis X label: "
+ self._state = InputState.StateDraggingX
+ elif type == QtGraphs3D.ElementType.AxisYLabel:
+ text += "Axis Y label: "
+ self._state = InputState.StateDraggingY
+ else:
+ text += "Axis Z label: "
+ self._state = InputState.StateDraggingZ
+ text += str(index)
+ self._textField.setText(text)
+ else:
+ self._textField.setText("Nothing")
+
+ def resetSelection(self):
+ self._selectionAnimation.stop()
+ if self._previouslyAnimatedItem:
+ self._previouslyAnimatedItem.setScaling(self._previousScaling)
+ self._previouslyAnimatedItem = None
+
+ def toggleModeNone(self):
+ self._graph.setSelectionMode(QtGraphs3D.SelectionFlag.None_)
+
+ def toggleModeItem(self):
+ self._graph.setSelectionMode(QtGraphs3D.SelectionFlag.Item)
+
+ def toggleModeSliceRow(self):
+ sm = (QtGraphs3D.SelectionFlag.ItemAndRow
+ | QtGraphs3D.SelectionFlag.Slice
+ | QtGraphs3D.SelectionFlag.MultiSeries)
+ self._graph.setSelectionMode(sm)
+
+ def toggleModeSliceColumn(self):
+ sm = (QtGraphs3D.SelectionFlag.ItemAndColumn
+ | QtGraphs3D.SelectionFlag.Slice
+ | QtGraphs3D.SelectionFlag.MultiSeries)
+ self._graph.setSelectionMode(sm)
+
+ def setAxisMinSliderX(self, slider):
+ self._axisMinSliderX = slider
+
+ def setAxisMaxSliderX(self, slider):
+ self._axisMaxSliderX = slider
+
+ def setAxisMinSliderZ(self, slider):
+ self._axisMinSliderZ = slider
+
+ def setAxisMaxSliderZ(self, slider):
+ self._axisMaxSliderZ = slider
+
+ def checkConstraints(self):
+ if self._axisXMinValue < self._areaMinValue:
+ self._axisXMinValue = self._areaMinValue
+ if self._axisXMaxValue > self._areaMaxValue:
+ self._axisXMaxValue = self._areaMaxValue
+ # Don't allow too much zoom in
+ range = self._axisXMaxValue - self._axisXMinValue
+ if range < self._axisXMinRange:
+ adjust = (self._axisXMinRange - range) / 2.0
+ self._axisXMinValue -= adjust
+ self._axisXMaxValue += adjust
+
+ if self._axisZMinValue < self._areaMinValue:
+ self._axisZMinValue = self._areaMinValue
+ if self._axisZMaxValue > self._areaMaxValue:
+ self._axisZMaxValue = self._areaMaxValue
+ # Don't allow too much zoom in
+ range = self._axisZMaxValue - self._axisZMinValue
+ if range < self._axisZMinRange:
+ adjust = (self._axisZMinRange - range) / 2.0
+ self._axisZMinValue -= adjust
+ self._axisZMaxValue += adjust
+
+ @Slot(QVector2D)
+ def handleAxisDragging(self, delta):
+
+ distance = float(0)
+
+ # Get scene orientation from active camera
+ xRotation = self._graph.cameraXRotation()
+
+ # Calculate directional drag multipliers based on rotation
+ xMulX = cos(degrees(xRotation))
+ xMulY = sin(degrees(xRotation))
+ zMulX = sin(degrees(xRotation))
+ zMulY = cos(degrees(xRotation))
+
+ # Get the drag amount
+ move = delta.toPoint()
+
+ # Adjust axes
+ if self._state == InputState.StateDraggingX:
+ distance = (move.x() * xMulX - move.y() * xMulY) * SPEED_MODIFIER
+ self._axisXMinValue -= distance
+ self._axisXMaxValue -= distance
+ if self._axisXMinValue < self._areaMinValue:
+ dist = self._axisXMaxValue - self._axisXMinValue
+ self._axisXMinValue = self._areaMinValue
+ self._axisXMaxValue = self._axisXMinValue + dist
+
+ if self._axisXMaxValue > self._areaMaxValue:
+ dist = self._axisXMaxValue - self._axisXMinValue
+ self._axisXMaxValue = self._areaMaxValue
+ self._axisXMinValue = self._axisXMaxValue - dist
+
+ self._graph.axisX().setRange(self._axisXMinValue, self._axisXMaxValue)
+ elif self._state == InputState.StateDraggingZ:
+ distance = (move.x() * zMulX + move.y() * zMulY) * SPEED_MODIFIER
+ self._axisZMinValue += distance
+ self._axisZMaxValue += distance
+ if self._axisZMinValue < self._areaMinValue:
+ dist = self._axisZMaxValue - self._axisZMinValue
+ self._axisZMinValue = self._areaMinValue
+ self._axisZMaxValue = self._axisZMinValue + dist
+
+ if self._axisZMaxValue > self._areaMaxValue:
+ dist = self._axisZMaxValue - self._axisZMinValue
+ self._axisZMaxValue = self._areaMaxValue
+ self._axisZMinValue = self._axisZMaxValue - dist
+
+ self._graph.axisZ().setRange(self._axisZMinValue, self._axisZMaxValue)
+
+ @Slot(QWheelEvent)
+ def onWheel(self, event):
+ delta = float(event.angleDelta().y())
+
+ self._axisXMinValue += delta
+ self._axisXMaxValue -= delta
+ self._axisZMinValue += delta
+ self._axisZMaxValue -= delta
+ self.checkConstraints()
+
+ y = (self._axisXMaxValue - self._axisXMinValue) * ASPECT_RATIO
+
+ self._graph.axisX().setRange(self._axisXMinValue, self._axisXMaxValue)
+ self._graph.axisY().setRange(100.0, y)
+ self._graph.axisZ().setRange(self._axisZMinValue, self._axisZMaxValue)
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from PySide6.QtCore import Qt
+from PySide6.QtGui import QImage, QVector3D
+from PySide6.QtGraphs import (QSurface3DSeries, QSurfaceDataItem)
+
+
+# Value used to encode height data as RGB value on PNG file
+PACKING_FACTOR = 11983.0
+
+
+class TopographicSeries(QSurface3DSeries):
+
+ def __init__(self):
+ super().__init__()
+ self._sampleCountX = 0.0
+ self._sampleCountZ = 0.0
+ self.setDrawMode(QSurface3DSeries.DrawFlag.DrawSurface)
+ self.setShading(QSurface3DSeries.Shading.Flat)
+ self.setBaseColor(Qt.GlobalColor.white)
+
+ def sampleCountX(self):
+ return self._sampleCountX
+
+ def sampleCountZ(self):
+ return self._sampleCountZ
+
+ def setTopographyFile(self, file, width, height):
+ heightMapImage = QImage(file)
+ bits = heightMapImage.bits()
+ imageHeight = heightMapImage.height()
+ imageWidth = heightMapImage.width()
+ widthBits = imageWidth * 4
+ stepX = width / float(imageWidth)
+ stepZ = height / float(imageHeight)
+
+ dataArray = []
+ for i in range(0, imageHeight):
+ p = i * widthBits
+ z = height - float(i) * stepZ
+ newRow = []
+ for j in range(0, imageWidth):
+ aa = bits[p + 0]
+ rr = bits[p + 1]
+ gg = bits[p + 2]
+ color = (gg << 16) + (rr << 8) + aa
+ y = float(color) / PACKING_FACTOR
+ item = QSurfaceDataItem(QVector3D(float(j) * stepX, y, z))
+ newRow.append(item)
+ p += 4
+ dataArray.append(newRow)
+
+ self.dataProxy().resetArray(dataArray)
+
+ self._sampleCountX = float(imageWidth)
+ self._sampleCountZ = float(imageHeight)
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from PySide6.QtCore import QObject, Signal
+
+
+class VariantBarDataMapping(QObject):
+
+ rowIndexChanged = Signal()
+ columnIndexChanged = Signal()
+ valueIndexChanged = Signal()
+ rowCategoriesChanged = Signal()
+ columnCategoriesChanged = Signal()
+ mappingChanged = Signal()
+
+ def __init__(self, rowIndex, columnIndex, valueIndex,
+ rowCategories=[], columnCategories=[]):
+ super().__init__(None)
+ self._rowIndex = rowIndex
+ self._columnIndex = columnIndex
+ self._valueIndex = valueIndex
+ self._rowCategories = rowCategories
+ self._columnCategories = columnCategories
+
+ def setRowIndex(self, index):
+ self._rowIndex = index
+ self.mappingChanged.emit()
+
+ def rowIndex(self):
+ return self._rowIndex
+
+ def setColumnIndex(self, index):
+ self._columnIndex = index
+ self.mappingChanged.emit()
+
+ def columnIndex(self):
+ return self._columnIndex
+
+ def setValueIndex(self, index):
+ self._valueIndex = index
+ self.mappingChanged.emit()
+
+ def valueIndex(self):
+ return self._valueIndex
+
+ def setRowCategories(self, categories):
+ self._rowCategories = categories
+ self.mappingChanged.emit()
+
+ def rowCategories(self):
+ return self._rowCategories
+
+ def setColumnCategories(self, categories):
+ self._columnCategories = categories
+ self.mappingChanged.emit()
+
+ def columnCategories(self):
+ return self._columnCategories
+
+ def remap(self, rowIndex, columnIndex, valueIndex,
+ rowCategories=[], columnCategories=[]):
+ self._rowIndex = rowIndex
+ self._columnIndex = columnIndex
+ self._valueIndex = valueIndex
+ self._rowCategories = rowCategories
+ self._columnCategories = columnCategories
+ self.mappingChanged.emit()
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from PySide6.QtCore import Slot
+from PySide6.QtGraphs import QBarDataProxy, QBarDataItem
+
+
+class VariantBarDataProxy(QBarDataProxy):
+
+ def __init__(self):
+ super().__init__()
+ self._dataSet = None
+ self._mapping = None
+
+ def setDataSet(self, newSet):
+ if self._dataSet:
+ self._dataSet.itemsAdded.disconnect(self.handleItemsAdded)
+ self._dataSet.dataCleared.disconnect(self.handleDataCleared)
+
+ self._dataSet = newSet
+
+ if self._dataSet:
+ self._dataSet.itemsAdded.connect(self.handleItemsAdded)
+ self._dataSet.dataCleared.connect(self.handleDataCleared)
+ self.resolveDataSet()
+
+ def dataSet(self):
+ return self._dataSet.data()
+
+ # Map key (row, column, value) to value index in data item (VariantItem).
+ # Doesn't gain ownership of mapping, but does connect to it to listen for
+ # mapping changes. Modifying mapping that is set to proxy will trigger
+ # dataset re-resolving.
+ def setMapping(self, mapping):
+ if self._mapping:
+ self._mapping.mappingChanged.disconnect(self.handleMappingChanged)
+
+ self._mapping = mapping
+
+ if self._mapping:
+ self._mapping.mappingChanged.connect(self.handleMappingChanged)
+
+ self.resolveDataSet()
+
+ def mapping(self):
+ return self._mapping.data()
+
+ @Slot(int, int)
+ def handleItemsAdded(self, index, count):
+ # Resolve new items
+ self.resolveDataSet()
+
+ @Slot()
+ def handleDataCleared(self):
+ # Data cleared, reset array
+ self.resetArray(None)
+
+ @Slot()
+ def handleMappingChanged(self):
+ self.resolveDataSet()
+
+ # Resolve entire dataset into QBarDataArray.
+ def resolveDataSet(self):
+ # If we have no data or mapping, or the categories are not defined,
+ # simply clear the array
+ if (not self._dataSet or not self._mapping
+ or not self._mapping.rowCategories()
+ or not self._mapping.columnCategories()):
+ self.resetArray()
+ return
+
+ itemList = self._dataSet.itemList()
+
+ rowIndex = self._mapping.rowIndex()
+ columnIndex = self._mapping.columnIndex()
+ valueIndex = self._mapping.valueIndex()
+ rowList = self._mapping.rowCategories()
+ columnList = self._mapping.columnCategories()
+
+ # Sort values into rows and columns
+ itemValueMap = {}
+ for item in itemList:
+ key = str(item[rowIndex])
+ v = itemValueMap.get(key)
+ if not v:
+ v = {}
+ itemValueMap[key] = v
+ v[str(item[columnIndex])] = float(item[valueIndex])
+
+ # Create a new data array in format the parent class understands
+ newProxyArray = []
+ for rowKey in rowList:
+ newProxyRow = []
+ for i in range(0, len(columnList)):
+ item = QBarDataItem(itemValueMap[rowKey][columnList[i]])
+ newProxyRow.append(item)
+ newProxyArray.append(newProxyRow)
+
+ # Finally, reset the data array in the parent class
+ self.resetArray(newProxyArray)
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+from PySide6.QtCore import QObject, Signal
+
+
+class VariantDataSet(QObject):
+
+ itemsAdded = Signal(int, int)
+ dataCleared = Signal()
+
+ def __init__(self):
+ super().__init__()
+ self._variantData = []
+
+ def clear(self):
+ for item in self._variantData:
+ item.clear()
+ del item
+
+ self._variantData.clear()
+ self.dataCleared.emit()
+
+ def addItem(self, item):
+ self._variantData.append(item)
+ addIndex = len(self._variantData)
+
+ self.itemsAdded.emit(addIndex, 1)
+ return addIndex
+
+ def addItems(self, itemList):
+ newCount = len(itemList)
+ addIndex = len(self._variantData)
+ self._variantData.extend(itemList)
+ self.itemsAdded.emit(addIndex, newCount)
+ return addIndex
+
+ def itemList(self):
+ return self._variantData
--- /dev/null
+{
+ "files": ["main.py",
+ "bargraph.py",
+ "graphmodifier.py",
+ "highlightseries.py",
+ "rainfalldata.py",
+ "scatterdatamodifier.py",
+ "scattergraph.py",
+ "surfacegraph.py",
+ "surfacegraphmodifier.py",
+ "topographicseries.py",
+ "variantbardatamapping.py",
+ "variantbardataproxy.py",
+ "variantdataset.py",
+ "data/layer_1.png",
+ "data/layer_2.png",
+ "data/layer_3.png",
+ "data/license.txt",
+ "data/maptexture.jpg",
+ "data/narrowarrow.mesh",
+ "data/oilrig.mesh",
+ "data/pipe.mesh",
+ "data/raindata.txt",
+ "data/refinery.mesh",
+ "data/topography.png"
+]
+}
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
QPoint(-1, -89), QPoint(1, -89)])
palette = qApp.palette() # noqa: F821
- self._background_color = palette.color(QPalette.Window)
- self._hour_color = palette.color(QPalette.Text)
- self._minute_color = palette.color(QPalette.Text)
- self._seconds_color = palette.color(QPalette.Accent)
+ self._background_color = palette.color(QPalette.ColorRole.Window)
+ self._hour_color = palette.color(QPalette.ColorRole.Text)
+ self._minute_color = palette.color(QPalette.ColorRole.Text)
+ self._seconds_color = palette.color(QPalette.ColorRole.Accent)
def paintEvent(self, e):
with QPainter(self) as painter:
side = min(width, height)
painter.fillRect(0, 0, width, height, self._background_color)
- painter.setRenderHint(QPainter.Antialiasing)
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
painter.translate(width / 2, height / 2)
painter.scale(side / 200.0, side / 200.0)
time = QTime.currentTime()
- painter.setPen(Qt.NoPen)
+ painter.setPen(Qt.PenStyle.NoPen)
painter.setBrush(self._hour_color)
painter.save()
-.. _rhi-window-example:
-
RHI Window Example
==================
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from argparse import ArgumentParser, RawDescriptionHelpFormatter
import sys
app = QGuiApplication(sys.argv)
# Use platform-specific defaults when no command-line arguments given.
- graphicsApi = QRhi.OpenGLES2
+ graphicsApi = QRhi.Implementation.OpenGLES2
if sys.platform == "win32":
- graphicsApi = QRhi.D3D11
+ graphicsApi = QRhi.Implementation.D3D11
elif sys.platform == "darwin":
- graphicsApi = QRhi.Metal
+ graphicsApi = QRhi.Implementation.Metal
parser = ArgumentParser(formatter_class=RawDescriptionHelpFormatter,
description="QRhi render example")
help="Metal")
args = parser.parse_args()
if args.null:
- graphicsApi = QRhi.Null
+ graphicsApi = QRhi.Implementation.Null
elif args.opengl:
- graphicsApi = QRhi.OpenGLES2
+ graphicsApi = QRhi.Implementation.OpenGLES2
elif args.d3d11:
- graphicsApi = QRhi.D3D11
+ graphicsApi = QRhi.Implementation.D3D11
elif args.d3d12:
- graphicsApi = QRhi.D3D12
+ graphicsApi = QRhi.Implementation.D3D12
elif args.metal:
- graphicsApi = QRhi.Metal
+ graphicsApi = QRhi.Implementation.Metal
# graphicsApi = QRhi.Vulkan? detect? needs QVulkanInstance
# we get on macOS is not sufficient.
if sys.platform == "darwin":
fmt.setVersion(4, 1)
- fmt.setProfile(QSurfaceFormat.CoreProfile)
+ fmt.setProfile(QSurfaceFormat.OpenGLContextProfile.CoreProfile)
QSurfaceFormat.setDefaultFormat(fmt)
window = HelloWindow(graphicsApi)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import numpy
import sys
def getShader(name):
f = QFile(name)
- if f.open(QIODevice.ReadOnly):
+ if f.open(QIODevice.OpenModeFlag.ReadOnly):
result = QShader.fromSerialized(f.readAll())
f.close()
return result
def __init__(self, graphicsApi):
super().__init__()
- self.m_graphicsApi = QRhi.Null
+ self.m_graphicsApi = QRhi.Implementation.Null
self.m_initialized = False
self.m_notExposed = False
self.m_newlyExposed = False
self.m_graphicsApi = graphicsApi
- if graphicsApi == QRhi.OpenGLES2:
+ if graphicsApi == QRhi.Implementation.OpenGLES2:
self.setSurfaceType(QSurface.SurfaceType.OpenGLSurface)
- elif graphicsApi == QRhi.Vulkan:
+ elif graphicsApi == QRhi.Implementation.Vulkan:
self.setSurfaceType(QSurface.SurfaceType.VulkanSurface)
- elif graphicsApi == QRhi.D3D11 or graphicsApi == QRhi.D3D12:
+ elif graphicsApi == QRhi.Implementation.D3D11 or graphicsApi == QRhi.Implementation.D3D12:
self.setSurfaceType(QSurface.SurfaceType.Direct3DSurface)
- elif graphicsApi == QRhi.Metal:
+ elif graphicsApi == QRhi.Implementation.Metal:
self.setSurfaceType(QSurface.SurfaceType.MetalSurface)
- elif graphicsApi == QRhi.Null:
+ elif graphicsApi == QRhi.Implementation.Null:
pass # RasterSurface
def __del__(self):
self.m_fallbackSurface = None
def graphicsApiName(self):
- if self.m_graphicsApi == QRhi.Null:
+ if self.m_graphicsApi == QRhi.Implementation.Null:
return "Null (no output)"
- if self.m_graphicsApi == QRhi.OpenGLES2:
+ if self.m_graphicsApi == QRhi.Implementation.OpenGLES2:
return "OpenGL"
- if self.m_graphicsApi == QRhi.Vulkan:
+ if self.m_graphicsApi == QRhi.Implementation.Vulkan:
return "Vulkan"
- if self.m_graphicsApi == QRhi.D3D11:
+ if self.m_graphicsApi == QRhi.Implementation.D3D11:
return "Direct3D 11"
- if self.m_graphicsApi == QRhi.D3D12:
+ if self.m_graphicsApi == QRhi.Implementation.D3D12:
return "Direct3D 12"
- if self.m_graphicsApi == QRhi.Metal:
+ if self.m_graphicsApi == QRhi.Implementation.Metal:
return "Metal"
return ""
self.render()
def event(self, e):
- if e.type() == QEvent.UpdateRequest:
+ if e.type() == QEvent.Type.UpdateRequest:
self.render()
- elif e.type() == QEvent.PlatformSurface:
+ elif e.type() == QEvent.Type.PlatformSurface:
# this is the proper time to tear down the swapchain (while
# the native window and surface are still around)
- if e.surfaceEventType() == QPlatformSurfaceEvent.SurfaceAboutToBeDestroyed:
+ if e.surfaceEventType() == QPlatformSurfaceEvent.SurfaceEventType.SurfaceAboutToBeDestroyed: # noqa: E501
self.releaseSwapChain()
return super().event(e)
def init(self):
- if self.m_graphicsApi == QRhi.Null:
+ if self.m_graphicsApi == QRhi.Implementation.Null:
params = QRhiNullInitParams()
- self.m_rhi = QRhi.create(QRhi.Null, params)
+ self.m_rhi = QRhi.create(QRhi.Implementation.Null, params)
- if self.m_graphicsApi == QRhi.OpenGLES2:
+ if self.m_graphicsApi == QRhi.Implementation.OpenGLES2:
self.m_fallbackSurface = QRhiGles2InitParams.newFallbackSurface()
params = QRhiGles2InitParams()
params.fallbackSurface = self.m_fallbackSurface
params.window = self
- self.m_rhi = QRhi.create(QRhi.OpenGLES2, params)
- elif self.m_graphicsApi == QRhi.D3D11:
+ self.m_rhi = QRhi.create(QRhi.Implementation.OpenGLES2, params)
+ elif self.m_graphicsApi == QRhi.Implementation.D3D11:
params = QRhiD3D11InitParams()
# Enable the debug layer, if available. This is optional
# and should be avoided in production builds.
params.enableDebugLayer = True
- self.m_rhi = QRhi.create(QRhi.D3D11, params)
- elif self.m_graphicsApi == QRhi.D3D12:
+ self.m_rhi = QRhi.create(QRhi.Implementation.D3D11, params)
+ elif self.m_graphicsApi == QRhi.Implementation.D3D12:
params = QRhiD3D12InitParams()
# Enable the debug layer, if available. This is optional
# and should be avoided in production builds.
params.enableDebugLayer = True
- self.m_rhi = QRhi.create(QRhi.D3D12, params)
- elif self.m_graphicsApi == QRhi.Metal:
+ self.m_rhi = QRhi.create(QRhi.Implementation.D3D12, params)
+ elif self.m_graphicsApi == QRhi.Implementation.Metal:
params = QRhiMetalInitParams()
- self.m_rhi.reset(QRhi.create(QRhi.Metal, params))
+ self.m_rhi.reset(QRhi.create(QRhi.Implementation.Metal, params))
if not self.m_rhi:
qFatal("Failed to create RHI backend")
self.m_sc = self.m_rhi.newSwapChain()
# no need to set the size here, due to UsedWithSwapChainOnly
- self.m_ds = self.m_rhi.newRenderBuffer(QRhiRenderBuffer.DepthStencil,
+ self.m_ds = self.m_rhi.newRenderBuffer(QRhiRenderBuffer.Type.DepthStencil,
QSize(), 1,
- QRhiRenderBuffer.UsedWithSwapChainOnly)
+ QRhiRenderBuffer.Flag.UsedWithSwapChainOnly)
self.m_sc.setWindow(self)
self.m_sc.setDepthStencil(self.m_ds)
self.m_rp = self.m_sc.newCompatibleRenderPassDescriptor()
self.m_newlyExposed = False
result = self.m_rhi.beginFrame(self.m_sc)
- if result == QRhi.FrameOpSwapChainOutOfDate:
+ if result == QRhi.FrameOpResult.FrameOpSwapChainOutOfDate:
self.resizeSwapChain()
if not self.m_hasSwapChain:
return
result = self.m_rhi.beginFrame(self.m_sc)
- if result != QRhi.FrameOpSuccess:
+ if result != QRhi.FrameOpResult.FrameOpSuccess:
qWarning(f"beginFrame failed with {result}, will retry")
self.requestUpdate()
return
return
if not self.m_texture:
- self.m_texture = self.m_rhi.newTexture(QRhiTexture.RGBA8, pixelSize)
+ self.m_texture = self.m_rhi.newTexture(QRhiTexture.Format.RGBA8, pixelSize)
else:
self.m_texture.setPixelSize(pixelSize)
self.m_texture.create()
- image = QImage(pixelSize, QImage.Format_RGBA8888_Premultiplied)
+ image = QImage(pixelSize, QImage.Format.Format_RGBA8888_Premultiplied)
with QPainter(image) as painter:
painter.fillRect(QRectF(QPointF(0, 0), pixelSize),
QColor.fromRgbF(0.4, 0.7, 0.0, 1.0))
- painter.setPen(Qt.transparent)
- painter.setBrush(QGradient(QGradient.DeepBlue))
+ painter.setPen(Qt.GlobalColor.transparent)
+ painter.setBrush(QGradient(QGradient.Preset.DeepBlue))
painter.drawRoundedRect(QRectF(QPointF(20, 20), pixelSize - QSize(40, 40)),
16, 16)
- painter.setPen(Qt.black)
+ painter.setPen(Qt.GlobalColor.black)
font = QFont()
font.setPixelSize(0.05 * min(pixelSize.width(), pixelSize.height()))
painter.setFont(font)
self.m_initialUpdates = self.m_rhi.nextResourceUpdateBatch()
vertex_size = 4 * VERTEX_DATA.size
- self.m_vbuf = self.m_rhi.newBuffer(QRhiBuffer.Immutable, QRhiBuffer.VertexBuffer,
+ self.m_vbuf = self.m_rhi.newBuffer(QRhiBuffer.Type.Immutable,
+ QRhiBuffer.UsageFlag.VertexBuffer,
vertex_size)
self.m_vbuf.create()
self.m_initialUpdates.uploadStaticBuffer(self.m_vbuf,
VoidPtr(VERTEX_DATA.tobytes(), vertex_size))
- self.m_ubuf = self.m_rhi.newBuffer(QRhiBuffer.Dynamic,
- QRhiBuffer.UniformBuffer, UBUF_SIZE)
+ self.m_ubuf = self.m_rhi.newBuffer(QRhiBuffer.Type.Dynamic,
+ QRhiBuffer.UsageFlag.UniformBuffer, UBUF_SIZE)
self.m_ubuf.create()
self.ensureFullscreenTexture(self.m_sc.surfacePixelSize(), self.m_initialUpdates)
- self.m_sampler = self.m_rhi.newSampler(QRhiSampler.Linear, QRhiSampler.Linear,
- QRhiSampler.None_,
- QRhiSampler.ClampToEdge, QRhiSampler.ClampToEdge)
+ self.m_sampler = self.m_rhi.newSampler(QRhiSampler.Filter.Linear,
+ QRhiSampler.Filter.Linear,
+ QRhiSampler.Filter.None_,
+ QRhiSampler.AddressMode.ClampToEdge,
+ QRhiSampler.AddressMode.ClampToEdge)
self.m_sampler.create()
self.m_colorTriSrb = self.m_rhi.newShaderResourceBindings()
- visibility = (QRhiShaderResourceBinding.VertexStage
- | QRhiShaderResourceBinding.FragmentStage)
+ visibility = (QRhiShaderResourceBinding.StageFlag.VertexStage
+ | QRhiShaderResourceBinding.StageFlag.FragmentStage)
bindings = [
QRhiShaderResourceBinding.uniformBuffer(0, visibility, self.m_ubuf)
]
premulAlphaBlend.enable = True
self.m_colorPipeline.setTargetBlends([premulAlphaBlend])
stages = [
- QRhiShaderStage(QRhiShaderStage.Vertex, getShader(":/color.vert.qsb")),
- QRhiShaderStage(QRhiShaderStage.Fragment, getShader(":/color.frag.qsb"))
+ QRhiShaderStage(QRhiShaderStage.Type.Vertex, getShader(":/color.vert.qsb")),
+ QRhiShaderStage(QRhiShaderStage.Type.Fragment, getShader(":/color.frag.qsb"))
]
self.m_colorPipeline.setShaderStages(stages)
inputLayout = QRhiVertexInputLayout()
input_bindings = [QRhiVertexInputBinding(5 * 4)] # sizeof(float)
inputLayout.setBindings(input_bindings)
attributes = [
- QRhiVertexInputAttribute(0, 0, QRhiVertexInputAttribute.Float2, 0),
- QRhiVertexInputAttribute(0, 1, QRhiVertexInputAttribute.Float3, 2 * 4)] # sizeof(float)
+ QRhiVertexInputAttribute(0, 0, QRhiVertexInputAttribute.Format.Float2, 0),
+ # sizeof(float)
+ QRhiVertexInputAttribute(0, 1, QRhiVertexInputAttribute.Format.Float3, 2 * 4)]
inputLayout.setAttributes(attributes)
self.m_colorPipeline.setVertexInputLayout(inputLayout)
self.m_colorPipeline.setShaderResourceBindings(self.m_colorTriSrb)
self.m_fullscreenQuadSrb = self.m_rhi.newShaderResourceBindings()
bindings = [
- QRhiShaderResourceBinding.sampledTexture(0, QRhiShaderResourceBinding.FragmentStage,
+ QRhiShaderResourceBinding.sampledTexture(0, QRhiShaderResourceBinding.StageFlag.FragmentStage, # noqa: E501
self.m_texture, self.m_sampler)
]
self.m_fullscreenQuadSrb.setBindings(bindings)
self.m_fullscreenQuadPipeline = self.m_rhi.newGraphicsPipeline()
stages = [
- QRhiShaderStage(QRhiShaderStage.Vertex, getShader(":/quad.vert.qsb")),
- QRhiShaderStage(QRhiShaderStage.Fragment, getShader(":/quad.frag.qsb"))
+ QRhiShaderStage(QRhiShaderStage.Type.Vertex, getShader(":/quad.vert.qsb")),
+ QRhiShaderStage(QRhiShaderStage.Type.Fragment, getShader(":/quad.frag.qsb"))
]
self.m_fullscreenQuadPipeline.setShaderStages(stages)
layout = QRhiVertexInputLayout()
self.ensureFullscreenTexture(outputSizeInPixels, resourceUpdates)
cv = QRhiDepthStencilClearValue(1.0, 0)
- cb.beginPass(self.m_sc.currentFrameRenderTarget(), Qt.black,
+ cb.beginPass(self.m_sc.currentFrameRenderTarget(), Qt.GlobalColor.black,
cv, resourceUpdates)
cb.setGraphicsPipeline(self.m_fullscreenQuadPipeline)
+++ /dev/null
-HTTP Server After Request Example
-=================================
-
-A Python application that demonstrates the analogous example in C++
-`AfterRequest Example <https://doc.qt.io/qt-6/qthttpserver-afterrequest-example.html>`_
+++ /dev/null
-# Copyright (C) 2020 Mikhail Svetkin <mikhail.svetkin@gmail.com>
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-"""PySide6 port of the /httpserver/afterrequest from from Qt"""
-
-import sys
-
-from PySide6.QtCore import QCoreApplication
-from PySide6.QtNetwork import QHostAddress
-from PySide6.QtHttpServer import QHttpServer
-
-
-def route(request):
- return "Hello world"
-
-
-def after_request(response, request):
- response.setHeader(b"Server", b"Super server!")
-
-
-if __name__ == '__main__':
- app = QCoreApplication(sys.argv)
- httpServer = QHttpServer()
- httpServer.route("/", route)
-
- httpServer.afterRequest(after_request)
-
- port = httpServer.listen(QHostAddress.Any)
- if port == 0:
- print("Server failed to listen on a port.", file=sys.stderr)
- sys.exit(-1)
-
- print(f"Running on http://127.0.0.1:{port}/ (Press CTRL+\\ to quit)")
-
- sys.exit(app.exec())
--- /dev/null
+Simple HTTP Server Example
+==========================
+
+A simplified version of the C++ example
+`Simple HTTP Server Example <https://doc.qt.io/qt-6/qthttpserver-simple-example.html>`_
--- /dev/null
+# Copyright (C) 2020 Mikhail Svetkin <mikhail.svetkin@gmail.com>
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
+
+"""PySide6 port of the /httpserver/afterrequest from from Qt"""
+
+import sys
+
+from PySide6.QtCore import QCoreApplication
+from PySide6.QtNetwork import QHttpHeaders, QTcpServer
+from PySide6.QtHttpServer import QHttpServer
+
+
+def route(request):
+ return "Hello world"
+
+
+def after_request(request, response):
+ headers = response.headers()
+ headers.append(QHttpHeaders.WellKnownHeader.WWWAuthenticate,
+ 'Basic realm="Simple example", charset="UTF-8"')
+ response.setHeaders(headers)
+
+
+if __name__ == '__main__':
+ app = QCoreApplication(sys.argv)
+ httpServer = QHttpServer()
+ httpServer.route("/", route)
+
+ httpServer.addAfterRequestHandler(httpServer, after_request)
+
+ tcpServer = QTcpServer()
+ if not tcpServer.listen() or not httpServer.bind(tcpServer):
+ print("Server failed to listen on a port.", file=sys.stderr)
+ sys.exit(-1)
+ port = tcpServer.serverPort()
+
+ print(f"Running on http://127.0.0.1:{port}/ (Press CTRL+\\ to quit)")
+
+ sys.exit(app.exec())
--- /dev/null
+{
+ "files": ["main.py"]
+}
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""
hello.py
self.button = QPushButton("Click me!")
self.text = QLabel(f"Hello World auto_quit={auto_quit}")
- self.text.setAlignment(Qt.AlignCenter)
+ self.text.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.layout = QVBoxLayout()
self.layout.addWidget(self.text)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the location/mapviewer example from Qt v6.x"""
QMetaObject.invokeMethod(items[0], "initializeProviders",
Q_ARG("QVariant", parameters))
- ex = application.exec()
+ exit_code = application.exec()
del engine
- sys.exit(ex)
+ sys.exit(exit_code)
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the multimedia/audiooutput example from Qt v5.x, originating from PyQt"""
self.generate_data(format, durationUs, sampleRate)
def start(self):
- self.open(QIODevice.ReadOnly)
+ self.open(QIODevice.OpenModeFlag.ReadOnly)
def stop(self):
self.m_pos = 0
sample_size = fmt.bytesPerSample() * 8
if sample_size == 8:
- if fmt.sampleFormat() == QAudioFormat.UInt8:
+ if fmt.sampleFormat() == QAudioFormat.SampleFormat.UInt8:
scaler = lambda x: ((1.0 + x) / 2 * 255) # noqa: E731
pack_format = 'B'
- elif fmt.sampleFormat() == QAudioFormat.Int16:
+ elif fmt.sampleFormat() == QAudioFormat.SampleFormat.Int16:
scaler = lambda x: x * 127 # noqa: E731
pack_format = 'b'
elif sample_size == 16:
- little_endian = QSysInfo.ByteOrder == QSysInfo.LittleEndian
- if fmt.sampleFormat() == QAudioFormat.UInt8:
+ little_endian = QSysInfo.Endian.ByteOrder == QSysInfo.Endian.LittleEndian
+ if fmt.sampleFormat() == QAudioFormat.SampleFormat.UInt8:
scaler = lambda x: (1.0 + x) / 2 * 65535 # noqa: E731
pack_format = '<H' if little_endian else '>H'
- elif fmt.sampleFormat() == QAudioFormat.Int16:
+ elif fmt.sampleFormat() == QAudioFormat.SampleFormat.Int16:
scaler = lambda x: x * 32767 # noqa: E731
pack_format = '<h' if little_endian else '>h'
volume_box = QHBoxLayout()
volume_label = QLabel("Volume:")
- self.m_volumeSlider = QSlider(Qt.Horizontal, minimum=0, maximum=100, singleStep=10)
+ self.m_volumeSlider = QSlider(Qt.Orientation.Horizontal,
+ minimum=0, maximum=100, singleStep=10)
self.m_volumeSlider.valueChanged.connect(self.volume_changed)
volume_box.addWidget(volume_label)
self.m_format = QAudioFormat()
self.m_format.setSampleRate(self.DATA_SAMPLE_RATE_HZ)
self.m_format.setChannelCount(1)
- self.m_format.setSampleFormat(QAudioFormat.Int16)
+ self.m_format.setSampleFormat(QAudioFormat.SampleFormat.Int16)
info = self.m_devices[0]
if not info.isFormatSupported(self.m_format):
@Slot()
def pull_timer_expired(self):
- if self.m_audioSink is not None and self.m_audioSink.state() != QAudio.StoppedState:
+ if self.m_audioSink is not None and self.m_audioSink.state() != QAudio.State.StoppedState:
bytes_free = self.m_audioSink.bytesFree()
data = self.m_generator.read(bytes_free)
if data:
@Slot()
def toggle_suspend_resume(self):
- if self.m_audioSink.state() == QAudio.SuspendedState:
+ if self.m_audioSink.state() == QAudio.State.SuspendedState:
qWarning("status: Suspended, resume()")
self.m_audioSink.resume()
self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
- elif self.m_audioSink.state() == QAudio.ActiveState:
+ elif self.m_audioSink.state() == QAudio.State.ActiveState:
qWarning("status: Active, suspend()")
self.m_audioSink.suspend()
self.m_suspendResumeButton.setText(self.RESUME_LABEL)
- elif self.m_audioSink.state() == QAudio.StoppedState:
+ elif self.m_audioSink.state() == QAudio.State.StoppedState:
qWarning("status: Stopped, resume()")
self.m_audioSink.resume()
self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
- elif self.m_audioSink.state() == QAudio.IdleState:
+ elif self.m_audioSink.state() == QAudio.State.IdleState:
qWarning("status: IdleState")
state_map = {
- QAudio.ActiveState: "ActiveState",
- QAudio.SuspendedState: "SuspendedState",
- QAudio.StoppedState: "StoppedState",
- QAudio.IdleState: "IdleState"}
+ QAudio.State.ActiveState: "ActiveState",
+ QAudio.State.SuspendedState: "SuspendedState",
+ QAudio.State.StoppedState: "StoppedState",
+ QAudio.State.IdleState: "IdleState"}
@Slot("QAudio::State")
def handle_state_changed(self, state):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""
PySide6 port of Qt6 example examples/multimedia/audiosources
"""
import os
import sys
-from typing import Optional
import PySide6
from PySide6.QtCore import QByteArray, QMargins, Qt, Slot, qWarning
class RenderArea(QWidget):
- def __init__(self, parent: Optional[PySide6.QtWidgets.QWidget] = None) -> None:
+ def __init__(self, parent: PySide6.QtWidgets.QWidget | None = None) -> None:
super().__init__(parent=parent)
self.m_level = 0
- self.setBackgroundRole(QPalette.Base)
+ self.setBackgroundRole(QPalette.ColorRole.Base)
self.setAutoFillBackground(True)
self.setMinimumHeight(30)
self.setMinimumWidth(200)
def paintEvent(self, event: PySide6.QtGui.QPaintEvent) -> None:
with QPainter(self) as painter:
- painter.setPen(Qt.black)
+ painter.setPen(Qt.GlobalColor.black)
frame = painter.viewport() - QMargins(10, 10, 10, 10)
painter.drawRect(frame)
return
pos: int = round((frame.width() - 1) * self.m_level)
- painter.fillRect(
- frame.left() + 1, frame.top() + 1, pos, frame.height() - 1, Qt.red
- )
+ painter.fillRect(frame.left() + 1, frame.top() + 1, pos, frame.height() - 1,
+ Qt.GlobalColor.red)
class InputTest(QWidget):
self.m_device_box.activated[int].connect(self.device_changed)
self.layout.addWidget(self.m_device_box)
- self.m_volume_slider = QSlider(Qt.Horizontal, self)
+ self.m_volume_slider = QSlider(Qt.Orientation.Horizontal, self)
self.m_volume_slider.setRange(0, 100)
self.m_volume_slider.setValue(100)
self.m_volume_slider.valueChanged.connect(self.slider_changed)
self.layout = QVBoxLayout(self)
error_label = QLabel(self.tr("Microphone permission is not granted!"))
error_label.setWordWrap(True)
- error_label.setAlignment(Qt.AlignCenter)
+ error_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.layout.addWidget(error_label)
def initialize_audio(self, device_info: QAudioDevice):
format = QAudioFormat()
format.setSampleRate(8000)
format.setChannelCount(1)
- format.setSampleFormat(QAudioFormat.Int16)
+ format.setSampleFormat(QAudioFormat.SampleFormat.Int16)
self.m_audio_info = AudioInfo(format)
self.m_audio_input = QAudioSource(device_info, format)
initial_volume = QAudio.convertVolume(
self.m_audio_input.volume(),
- QAudio.LinearVolumeScale,
- QAudio.LogarithmicVolumeScale,
+ QAudio.VolumeScale.LinearVolumeScale,
+ QAudio.VolumeScale.LogarithmicVolumeScale,
)
self.m_volume_slider.setValue(int(round(initial_volume * 100)))
self.toggle_mode()
def toggle_suspend(self):
# toggle suspend/resume
state = self.m_audio_input.state()
- if (state == QAudio.SuspendedState) or (state == QAudio.StoppedState):
+ if (state == QAudio.State.SuspendedState) or (state == QAudio.State.StoppedState):
self.m_audio_input.resume()
self.m_suspend_resume_button.setText("Suspend recording")
- elif state == QAudio.ActiveState:
+ elif state == QAudio.State.ActiveState:
self.m_audio_input.suspend()
self.m_suspend_resume_button.setText("Resume recording")
# else no-op
@Slot(int)
def slider_changed(self, value):
- linearVolume = QAudio.convertVolume(
- value / float(100), QAudio.LogarithmicVolumeScale, QAudio.LinearVolumeScale
- )
-
+ linearVolume = QAudio.convertVolume(value / float(100),
+ QAudio.VolumeScale.LogarithmicVolumeScale,
+ QAudio.VolumeScale.LinearVolumeScale)
self.m_audio_input.setVolume(linearVolume)
.. tags:: Android
A Python application that demonstrates the analogous example in C++
-`Audio Source Example <https://doc-snapshots.qt.io/qt6-dev/qtmultimedia-multimedia-audiosource-example.html>`_
+`Audio Source Example <https://doc.qt.io/qt-6/qtmultimedia-audiosource-example.html>`_
.. image:: audiosource.png
:width: 400
:alt: audiosource example
-
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
import sys
return
key = event.key()
- if key == Qt.Key_CameraFocus:
+ if key == Qt.Key.Key_CameraFocus:
self.displayViewfinder()
event.accept()
- elif key == Qt.Key_Camera:
+ elif key == Qt.Key.Key_Camera:
if self.m_doImageCapture:
self.takeImage()
else:
@Slot(int, QImage)
def processCapturedImage(self, requestId, img):
- scaled_image = img.scaled(self._ui.viewfinder.size(), Qt.KeepAspectRatio,
- Qt.SmoothTransformation)
+ scaled_image = img.scaled(self._ui.viewfinder.size(),
+ Qt.AspectRatioMode.KeepAspectRatio,
+ Qt.TransformationMode.SmoothTransformation)
self._ui.lastImagePreviewLabel.setPixmap(QPixmap.fromImage(scaled_image))
@Slot(QMediaRecorder.RecorderState)
def updateRecorderState(self, state):
- if state == QMediaRecorder.StoppedState:
+ if state == QMediaRecorder.RecorderState.StoppedState:
self._ui.recordButton.setEnabled(True)
self._ui.pauseButton.setEnabled(True)
self._ui.stopButton.setEnabled(False)
self._ui.metaDataButton.setEnabled(True)
- elif state == QMediaRecorder.PausedState:
+ elif state == QMediaRecorder.RecorderState.PausedState:
self._ui.recordButton.setEnabled(True)
self._ui.pauseButton.setEnabled(False)
self._ui.stopButton.setEnabled(True)
self._ui.metaDataButton.setEnabled(False)
- elif state == QMediaRecorder.RecordingState:
+ elif state == QMediaRecorder.RecorderState.RecordingState:
self._ui.recordButton.setEnabled(False)
self._ui.pauseButton.setEnabled(True)
self._ui.stopButton.setEnabled(True)
def showMetaDataDialog(self):
if not self.m_metaDataDialog:
self.m_metaDataDialog = MetaDataDialog(self)
- self.m_metaDataDialog.setAttribute(Qt.WA_DeleteOnClose, False)
- if self.m_metaDataDialog.exec() == QDialog.Accepted:
+ self.m_metaDataDialog.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, False)
+ if self.m_metaDataDialog.exec() == QDialog.DialogCode.Accepted:
self.saveMetaData()
@Slot()
val = self.m_metaDataDialog.m_metaDataFields[i].text()
if val:
key = QMediaMetaData.Key(i)
- if key == QMediaMetaData.CoverArtImage:
+ if key == QMediaMetaData.Key.CoverArtImage:
cover_art = QImage(val)
data.insert(key, cover_art)
- elif key == QMediaMetaData.ThumbnailImage:
+ elif key == QMediaMetaData.Key.ThumbnailImage:
thumbnail = QImage(val)
data.insert(key, thumbnail)
- elif key == QMediaMetaData.Date:
+ elif key == QMediaMetaData.Key.Date:
date = QDateTime.fromString(val)
data.insert(key, date)
else:
<item row="3" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
- <enum>Qt::Vertical</enum>
+ <enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<number>2</number>
</property>
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
- <enum>QSlider::TicksAbove</enum>
+ <enum>QSlider::TickPosition::TicksAbove</enum>
</property>
</widget>
</item>
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
- <enum>Qt::Vertical</enum>
+ <enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
</sizepolicy>
</property>
<property name="frameShape">
- <enum>QFrame::Box</enum>
+ <enum>QFrame::Shape::Box</enum>
</property>
<property name="text">
<string/>
<x>0</x>
<y>0</y>
<width>668</width>
- <height>19</height>
+ <height>26</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<number>2</number>
</property>
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="tickPosition">
- <enum>QSlider::TicksAbove</enum>
+ <enum>QSlider::TickPosition::TicksAbove</enum>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
- <enum>Qt::Vertical</enum>
+ <enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
</sizepolicy>
</property>
<property name="frameShape">
- <enum>QFrame::Box</enum>
+ <enum>QFrame::Shape::Box</enum>
</property>
<property name="text">
<string/>
<x>0</x>
<y>0</y>
<width>668</width>
- <height>22</height>
+ <height>26</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtMultimedia import QImageCapture
from PySide6.QtWidgets import QDialog
<number>4</number>
</property>
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
- <enum>Qt::Vertical</enum>
+ <enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<item row="2" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the QtMultiMedia camera example from Qt v6.x"""
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtMultimedia import QMediaMetaData
from PySide6.QtWidgets import (QDialog, QDialogButtonBox, QFileDialog,
def default_value(key):
- if key == QMediaMetaData.Title:
+ if key == QMediaMetaData.Key.Title:
return "Qt Camera Example"
- if key == QMediaMetaData.Author:
+ if key == QMediaMetaData.Key.Author:
return "The Qt Company"
- if key == QMediaMetaData.Date:
+ if key == QMediaMetaData.Key.Date:
return QDateTime.currentDateTime().toString()
return ""
line_edit = QLineEdit(default_value(key))
line_edit.setClearButtonEnabled(True)
self.m_metaDataFields.append(line_edit)
- if key == QMediaMetaData.ThumbnailImage:
+ if key == QMediaMetaData.Key.ThumbnailImage:
open_thumbnail = QPushButton("Open")
open_thumbnail.clicked.connect(self.open_thumbnail_image)
layout = QHBoxLayout()
layout.addWidget(line_edit)
layout.addWidget(open_thumbnail)
meta_data_layout.addRow(label, layout)
- elif key == QMediaMetaData.CoverArtImage:
+ elif key == QMediaMetaData.Key.CoverArtImage:
open_cover_art = QPushButton("Open")
open_cover_art.clicked.connect(self.open_cover_art_image)
layout = QHBoxLayout()
dialog_layout = QVBoxLayout(self)
dialog_layout.addWidget(scroll_area)
- button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok
+ | QDialogButtonBox.StandardButton.Cancel)
dialog_layout.addWidget(button_box)
self.setWindowTitle("Set Metadata")
################################################################################
## Form generated from reading UI file 'camera.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.exposureCompensation.setMinimum(-4)
self.exposureCompensation.setMaximum(4)
self.exposureCompensation.setPageStep(2)
- self.exposureCompensation.setOrientation(Qt.Horizontal)
- self.exposureCompensation.setTickPosition(QSlider.TicksAbove)
+ self.exposureCompensation.setOrientation(Qt.Orientation.Horizontal)
+ self.exposureCompensation.setTickPosition(QSlider.TickPosition.TicksAbove)
self.gridLayout.addWidget(self.exposureCompensation, 5, 0, 1, 1)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.lastImagePreviewLabel.sizePolicy().hasHeightForWidth())
self.lastImagePreviewLabel.setSizePolicy(sizePolicy1)
- self.lastImagePreviewLabel.setFrameShape(QFrame.Box)
+ self.lastImagePreviewLabel.setFrameShape(QFrame.Shape.Box)
self.gridLayout_4.addWidget(self.lastImagePreviewLabel, 0, 0, 1, 1)
Camera.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(Camera)
self.menubar.setObjectName(u"menubar")
- self.menubar.setGeometry(QRect(0, 0, 668, 19))
+ self.menubar.setGeometry(QRect(0, 0, 668, 26))
self.menuFile = QMenu(self.menubar)
self.menuFile.setObjectName(u"menuFile")
self.menuDevices = QMenu(self.menubar)
################################################################################
## Form generated from reading UI file 'camera_mobile.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.exposureCompensation.setMinimum(-4)
self.exposureCompensation.setMaximum(4)
self.exposureCompensation.setPageStep(2)
- self.exposureCompensation.setOrientation(Qt.Horizontal)
- self.exposureCompensation.setTickPosition(QSlider.TicksAbove)
+ self.exposureCompensation.setOrientation(Qt.Orientation.Horizontal)
+ self.exposureCompensation.setTickPosition(QSlider.TickPosition.TicksAbove)
self.gridLayout.addWidget(self.exposureCompensation, 4, 0, 1, 1)
self.takeImageButton.setObjectName(u"takeImageButton")
self.takeImageButton.setEnabled(False)
icon = QIcon()
- icon.addFile(u":/images/shutter.svg", QSize(), QIcon.Normal, QIcon.Off)
+ icon.addFile(u":/images/shutter.svg", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.takeImageButton.setIcon(icon)
self.gridLayout.addWidget(self.takeImageButton, 0, 0, 1, 1)
sizePolicy4.setVerticalStretch(0)
sizePolicy4.setHeightForWidth(self.lastImagePreviewLabel.sizePolicy().hasHeightForWidth())
self.lastImagePreviewLabel.setSizePolicy(sizePolicy4)
- self.lastImagePreviewLabel.setFrameShape(QFrame.Box)
+ self.lastImagePreviewLabel.setFrameShape(QFrame.Shape.Box)
self.gridLayout_4.addWidget(self.lastImagePreviewLabel, 0, 0, 1, 1)
Camera.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(Camera)
self.menubar.setObjectName(u"menubar")
- self.menubar.setGeometry(QRect(0, 0, 668, 22))
+ self.menubar.setGeometry(QRect(0, 0, 668, 26))
self.menuFile = QMenu(self.menubar)
self.menuFile.setObjectName(u"menuFile")
self.menuDevices = QMenu(self.menubar)
################################################################################
## Form generated from reading UI file 'imagesettings.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.imageQualitySlider = QSlider(self.groupBox_2)
self.imageQualitySlider.setObjectName(u"imageQualitySlider")
self.imageQualitySlider.setMaximum(4)
- self.imageQualitySlider.setOrientation(Qt.Horizontal)
+ self.imageQualitySlider.setOrientation(Qt.Orientation.Horizontal)
self.gridLayout_2.addWidget(self.imageQualitySlider, 4, 1, 1, 1)
self.buttonBox = QDialogButtonBox(ImageSettingsUi)
self.buttonBox.setObjectName(u"buttonBox")
- self.buttonBox.setOrientation(Qt.Horizontal)
- self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
+ self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
+ self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok)
self.gridLayout.addWidget(self.buttonBox, 2, 0, 1, 1)
################################################################################
## Form generated from reading UI file 'videosettings.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.gridLayout_3.setObjectName(u"gridLayout_3")
self.buttonBox = QDialogButtonBox(VideoSettingsUi)
self.buttonBox.setObjectName(u"buttonBox")
- self.buttonBox.setOrientation(Qt.Horizontal)
- self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
+ self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
+ self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok)
self.gridLayout_3.addWidget(self.buttonBox, 4, 1, 1, 1)
self.fpsSlider = QSlider(self.groupBox_2)
self.fpsSlider.setObjectName(u"fpsSlider")
- self.fpsSlider.setOrientation(Qt.Horizontal)
+ self.fpsSlider.setOrientation(Qt.Orientation.Horizontal)
self.horizontalLayout.addWidget(self.fpsSlider)
self.qualitySlider = QSlider(self.groupBox)
self.qualitySlider.setObjectName(u"qualitySlider")
self.qualitySlider.setMaximum(4)
- self.qualitySlider.setOrientation(Qt.Horizontal)
+ self.qualitySlider.setOrientation(Qt.Orientation.Horizontal)
self.verticalLayout.addWidget(self.qualitySlider)
################################################################################
## Form generated from reading UI file 'videosettings_mobile.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
def setupUi(self, VideoSettingsUi):
if not VideoSettingsUi.objectName():
VideoSettingsUi.setObjectName(u"VideoSettingsUi")
- VideoSettingsUi.resize(329, 591)
+ VideoSettingsUi.resize(329, 610)
self.gridLayout_3 = QGridLayout(VideoSettingsUi)
self.gridLayout_3.setObjectName(u"gridLayout_3")
self.widget = QWidget(VideoSettingsUi)
self.qualitySlider = QSlider(self.groupBox)
self.qualitySlider.setObjectName(u"qualitySlider")
self.qualitySlider.setMaximum(4)
- self.qualitySlider.setOrientation(Qt.Horizontal)
+ self.qualitySlider.setOrientation(Qt.Orientation.Horizontal)
self.verticalLayout.addWidget(self.qualitySlider)
self.buttonBox = QDialogButtonBox(self.groupBox_2)
self.buttonBox.setObjectName(u"buttonBox")
- self.buttonBox.setOrientation(Qt.Horizontal)
- self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
+ self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
+ self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok)
self.gridLayout_2.addWidget(self.buttonBox, 7, 0, 1, 1)
self.fpsSlider = QSlider(self.groupBox_2)
self.fpsSlider.setObjectName(u"fpsSlider")
- self.fpsSlider.setOrientation(Qt.Horizontal)
+ self.fpsSlider.setOrientation(Qt.Orientation.Horizontal)
self.horizontalLayout.addWidget(self.fpsSlider)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
from PySide6.QtMultimedia import (QCameraFormat, QMediaFormat, QMediaRecorder,
<item row="4" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="fpsSlider">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
<number>4</number>
</property>
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
- <enum>Qt::Vertical</enum>
+ <enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<x>0</x>
<y>0</y>
<width>329</width>
- <height>591</height>
+ <height>610</height>
</rect>
</property>
<property name="windowTitle">
<number>4</number>
</property>
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="fpsSlider">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 Multimedia player example"""
from PySide6.QtWidgets import (QApplication, QDialog, QFileDialog,
QMainWindow, QSlider, QStyle, QToolBar)
from PySide6.QtMultimedia import (QAudioOutput, QMediaFormat,
- QMediaPlayer)
+ QMediaPlayer, QAudio)
from PySide6.QtMultimediaWidgets import QVideoWidget
play_menu.addAction(self._stop_action)
self._volume_slider = QSlider()
- self._volume_slider.setOrientation(Qt.Horizontal)
+ self._volume_slider.setOrientation(Qt.Orientation.Horizontal)
self._volume_slider.setMinimum(0)
self._volume_slider.setMaximum(100)
available_width = self.screen().availableGeometry().width()
self._volume_slider.setFixedWidth(available_width / 10)
- self._volume_slider.setValue(self._audio_output.volume())
+ self._volume_slider.setValue(self._audio_output.volume() * 100)
self._volume_slider.setTickInterval(10)
self._volume_slider.setTickPosition(QSlider.TicksBelow)
self._volume_slider.setToolTip("Volume")
- self._volume_slider.valueChanged.connect(self._audio_output.setVolume)
+ self._volume_slider.valueChanged.connect(self.setVolume)
tool_bar.addWidget(self._volume_slider)
icon = QIcon.fromTheme(QIcon.ThemeIcon.HelpAbout)
print(error_string, file=sys.stderr)
self.show_status_message(error_string)
+ @Slot()
+ def setVolume(self):
+ self.volumeValue = QAudio.convertVolume(self._volume_slider.value() / 100.0,
+ QAudio.VolumeScale.LogarithmicVolumeScale,
+ QAudio.VolumeScale.LinearVolumeScale)
+ self._audio_output.setVolume(self.volumeValue)
+
if __name__ == '__main__':
app = QApplication(sys.argv)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the QtMultiMedia Screen Capture Example from Qt v6.x"""
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from enum import Enum, auto
update_action = QAction("Update windows List", self)
update_action.triggered.connect(self._window_list_model.populate)
self._window_list_view.addAction(update_action)
- self._window_list_view.setContextMenuPolicy(Qt.ActionsContextMenu)
+ self._window_list_view.setContextMenuPolicy(Qt.ContextMenuPolicy.ActionsContextMenu)
grid_layout = QGridLayout(self)
grid_layout.addWidget(self._screen_label, 0, 0)
self._start_stop_button.clicked.connect(self.on_start_stop_button_clicked)
self._screen_capture.errorOccurred.connect(self.on_screen_capture_error_occured,
- Qt.QueuedConnection)
+ Qt.ConnectionType.QueuedConnection)
self._window_capture.errorOccurred.connect(self.on_window_capture_error_occured,
- Qt.QueuedConnection)
+ Qt.ConnectionType.QueuedConnection)
self.update_active(SourceType.Screen, True)
@Slot(QItemSelection)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtGui import QGuiApplication
from PySide6.QtCore import QAbstractListModel, Qt, Slot
def data(self, index, role):
screen_list = QGuiApplication.screens()
- if role == Qt.DisplayRole:
+ if role == Qt.ItemDataRole.DisplayRole:
screen = screen_list[index.row()]
w = screen.size().width()
h = screen.size().height()
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QAbstractListModel, Qt, Slot
from PySide6.QtMultimedia import QWindowCapture
return len(self._window_list)
def data(self, index, role):
- if role == Qt.DisplayRole:
+ if role == Qt.ItemDataRole.DisplayRole:
window = self._window_list[index.row()]
return window.description()
return None
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the network/blockingfortunclient example from Qt v5.x, originating from PyQt"""
port_label = QLabel("S&erver port:")
for ip_address in QNetworkInterface.allAddresses():
- if ip_address != QHostAddress.LocalHost and ip_address.toIPv4Address() != 0:
+ if (ip_address != QHostAddress.SpecialAddress.LocalHost
+ and ip_address.toIPv4Address() != 0):
break
else:
- ip_address = QHostAddress(QHostAddress.LocalHost)
+ ip_address = QHostAddress(QHostAddress.SpecialAddress.LocalHost)
ip_address = ip_address.toString()
quit_button = QPushButton("Quit")
button_box = QDialogButtonBox()
- button_box.addButton(self._get_fortune_button, QDialogButtonBox.ActionRole)
- button_box.addButton(quit_button, QDialogButtonBox.RejectRole)
+ button_box.addButton(self._get_fortune_button, QDialogButtonBox.ButtonRole.ActionRole)
+ button_box.addButton(quit_button, QDialogButtonBox.ButtonRole.RejectRole)
self._get_fortune_button.clicked.connect(self.request_new_fortune)
quit_button.clicked.connect(self.close)
self._get_fortune_button.setEnabled(True)
def display_error(self, socketError, message):
- if socketError == QAbstractSocket.HostNotFoundError:
+ if socketError == QAbstractSocket.SocketError.HostNotFoundError:
QMessageBox.information(self, "Blocking Fortune Client",
"The host was not found. Please check the host and port "
"settings.")
- elif socketError == QAbstractSocket.ConnectionRefusedError:
+ elif socketError == QAbstractSocket.SocketError.ConnectionRefusedError:
QMessageBox.information(self, "Blocking Fortune Client",
"The connection was refused by the peer. Make sure the "
"fortune server is running, and check that the host name "
--- /dev/null
+Blocking Fortune Client Example
+===============================
+
+Demonstrates how to create a client for a network service. It corresponds to the
+Qt example `Blocking Fortune Client Example
+<https://doc.qt.io/qt-6/qtnetwork-blockingfortuneclient-example.html>`_.
+
+.. image:: blockingfortuneclient.png
+ :align: center
+ :alt: blockingfortuneclient screenshot
+ :width: 400
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWidgets import (
QWidget,
self.link_box.setPlaceholderText("Download Link ...")
self._open_folder_action = self.dest_box.addAction(
- qApp.style().standardIcon(QStyle.SP_DirOpenIcon), # noqa: F821
- QLineEdit.TrailingPosition
+ qApp.style().standardIcon(QStyle.StandardPixmap.SP_DirOpenIcon), # noqa: F821
+ QLineEdit.ActionPosition.TrailingPosition
)
self._open_folder_action.triggered.connect(self.on_open_folder)
# Â Default destination dir
self.dest_box.setText(
QDir.fromNativeSeparators(
- QStandardPaths.writableLocation(QStandardPaths.DownloadLocation)
+ QStandardPaths.writableLocation(QStandardPaths.StandardLocation.DownloadLocation)
)
)
# Create the file in write mode to append bytes
self.file = QSaveFile(dest_file)
- if self.file.open(QIODevice.WriteOnly):
+ if self.file.open(QIODevice.OpenModeFlag.WriteOnly):
# Start a GET HTTP request
self.reply = self.manager.get(QNetworkRequest(url_file))
def on_ready_read(self):
""" Get available bytes and store them into the file"""
if self.reply:
- if self.reply.error() == QNetworkReply.NoError:
+ if self.reply.error() == QNetworkReply.NetworkError.NoError:
self.file.write(self.reply.readAll())
@Slot()
def on_open_folder(self):
dir_path = QFileDialog.getExistingDirectory(
- self, "Open Directory", QDir.homePath(), QFileDialog.ShowDirsOnly
+ self, "Open Directory", QDir.homePath(), QFileDialog.Option.ShowDirsOnly
)
if dir_path:
--- /dev/null
+Fortune Client Example
+======================
+
+Demonstrates how to create a client for a network service. It corresponds to the
+Qt example `Fortune Client Example
+<https://doc.qt.io/qt-6/qtnetwork-fortuneclient-example.html>`_.
+
+.. image:: fortuneclient.png
+ :align: center
+ :alt: fortuneclient screenshot
+ :width: 400
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the network/fortuneclient example from Qt v5.x"""
quit_button = QPushButton("Quit")
button_box = QDialogButtonBox()
- button_box.addButton(self._get_fortune_button, QDialogButtonBox.ActionRole)
- button_box.addButton(quit_button, QDialogButtonBox.RejectRole)
+ button_box.addButton(self._get_fortune_button, QDialogButtonBox.ButtonRole.ActionRole)
+ button_box.addButton(quit_button, QDialogButtonBox.ButtonRole.RejectRole)
self._tcp_socket = QTcpSocket(self)
def read_fortune(self):
instr = QDataStream(self._tcp_socket)
- instr.setVersion(QDataStream.Qt_4_0)
+ instr.setVersion(QDataStream.Version.Qt_4_0)
if self._block_size == 0:
if self._tcp_socket.bytesAvailable() < 2:
self._get_fortune_button.setEnabled(True)
def display_error(self, socketError):
- if socketError == QAbstractSocket.RemoteHostClosedError:
+ if socketError == QAbstractSocket.SocketError.RemoteHostClosedError:
pass
- elif socketError == QAbstractSocket.HostNotFoundError:
+ elif socketError == QAbstractSocket.SocketError.HostNotFoundError:
QMessageBox.information(self, "Fortune Client",
"The host was not found. Please check the host name and "
"port settings.")
- elif socketError == QAbstractSocket.ConnectionRefusedError:
+ elif socketError == QAbstractSocket.SocketError.ConnectionRefusedError:
QMessageBox.information(self, "Fortune Client",
"The connection was refused by the peer. Make sure the "
"fortune server is running, and check that the host name "
--- /dev/null
+Fortune Server Example
+======================
+
+Demonstrates how to create a server for a network service. It corresponds to the
+Qt example `Fortune Server Example
+<https://doc.qt.io/qt-6/qtnetwork-fortuneserver-example.html>`_.
+
+.. image:: fortuneserver.png
+ :align: center
+ :alt: fortuneserver screenshot
+ :width: 400
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the network/fortuneserver example from Qt v5.x"""
super().__init__(parent)
status_label = QLabel()
- status_label.setTextInteractionFlags(Qt.TextBrowserInteraction)
+ status_label.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction)
quit_button = QPushButton("Quit")
quit_button.setAutoDefault(False)
def send_fortune(self):
block = QByteArray()
- out = QDataStream(block, QIODevice.WriteOnly)
- out.setVersion(QDataStream.Qt_4_0)
+ out = QDataStream(block, QIODevice.OpenModeFlag.WriteOnly)
+ out.setVersion(QDataStream.Version.Qt_4_0)
out.writeUInt16(0)
fortune = self.fortunes[random.randint(0, len(self.fortunes) - 1)]
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from typing import List
+from __future__ import annotations
from PySide6.QtCore import (QByteArray, QEvent, QObject, QPoint, Qt, QTimer,
QXmlStreamReader, Slot)
from PySide6.QtGui import QPalette
from PySide6.QtNetwork import (QNetworkAccessManager, QNetworkReply,
QNetworkRequest)
-from PySide6.QtWidgets import QFrame, QTreeWidget, QTreeWidgetItem
+from PySide6.QtWidgets import QAbstractItemView, QFrame, QTreeWidget, QTreeWidgetItem
class GSuggestCompletion(QObject):
super().__init__(parent)
self.editor = parent
self.popup = QTreeWidget()
- self.popup.setWindowFlags(Qt.Popup)
- self.popup.setFocusPolicy(Qt.NoFocus)
+ self.popup.setWindowFlags(Qt.WindowType.Popup)
+ self.popup.setFocusPolicy(Qt.FocusPolicy.NoFocus)
self.popup.setFocusProxy(parent)
self.popup.setMouseTracking(True)
self.popup.setColumnCount(1)
self.popup.setUniformRowHeights(True)
self.popup.setRootIsDecorated(False)
- self.popup.setEditTriggers(QTreeWidget.NoEditTriggers)
- self.popup.setSelectionBehavior(QTreeWidget.SelectRows)
- self.popup.setFrameStyle(QFrame.Box | QFrame.Plain)
- self.popup.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self.popup.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
+ self.popup.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
+ self.popup.setFrameStyle(QFrame.Shape.Box | QFrame.Shadow.Plain)
+ self.popup.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.popup.header().hide()
self.popup.installEventFilter(self)
def eventFilter(self, obj: QObject, ev: QEvent):
if obj is not self.popup:
return False
- if ev.type() == QEvent.MouseButtonPress:
+ if ev.type() == QEvent.Type.MouseButtonPress:
self.popup.hide()
self.editor.setFocus()
return True
- if ev.type() == QEvent.KeyPress:
+ if ev.type() == QEvent.Type.KeyPress:
consumed = False
key = ev.key()
- if key in (Qt.Key_Enter, Qt.Key_Return):
+ if key in (Qt.Key.Key_Enter, Qt.Key.Key_Return):
self.done_completion()
consumed = True
- elif key == Qt.Key_Escape:
+ elif key == Qt.Key.Key_Escape:
self.editor.setFocus()
self.popup.hide()
consumed = True
elif key in (
- Qt.Key_Up,
- Qt.Key_Down,
- Qt.Key_Home,
- Qt.Key_End,
- Qt.Key_PageUp,
- Qt.Key_PageDown,
+ Qt.Key.Key_Up,
+ Qt.Key.Key_Down,
+ Qt.Key.Key_Home,
+ Qt.Key.Key_End,
+ Qt.Key.Key_PageUp,
+ Qt.Key.Key_PageDown,
):
pass
else:
return consumed
return False
- def show_completion(self, choices: List[str]):
+ def show_completion(self, choices: list[str]):
if not choices:
return
pal = self.editor.palette()
- color = pal.color(QPalette.Disabled, QPalette.WindowText)
+ color = pal.color(QPalette.ColorGroup.Disabled, QPalette.ColorRole.WindowText)
self.popup.setUpdatesEnabled(False)
self.popup.clear()
@Slot(QNetworkReply)
def handle_network_data(self, network_reply: QNetworkReply):
- if network_reply.error() == QNetworkReply.NoError:
- choices: List[str] = []
+ if network_reply.error() == QNetworkReply.NetworkError.NoError:
+ choices: list[str] = []
response: QByteArray = network_reply.readAll()
xml = QXmlStreamReader(str(response))
while not xml.atEnd():
xml.readNext()
- if xml.tokenType() == QXmlStreamReader.StartElement:
+ if xml.tokenType() == QXmlStreamReader.TokenType.StartElement:
if xml.name() == "suggestion":
s = xml.attributes().value("data")
choices.append(s)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Slot
from PySide6.QtGui import QDesktopServices
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QByteArray, Qt
from PySide6.QtGui import QGuiApplication
self.quit_button = QPushButton("&Quit")
self.button_box = QDialogButtonBox()
- self.button_box.addButton(self.start_button, QDialogButtonBox.ActionRole)
- self.button_box.addButton(self.quit_button, QDialogButtonBox.RejectRole)
+ self.button_box.addButton(self.start_button, QDialogButtonBox.ButtonRole.ActionRole)
+ self.button_box.addButton(self.quit_button, QDialogButtonBox.ButtonRole.RejectRole)
self.start_button.clicked.connect(self.start)
self.quit_button.clicked.connect(self.close)
self.start_button.setEnabled(False)
- QGuiApplication.setOverrideCursor(Qt.WaitCursor)
+ QGuiApplication.setOverrideCursor(Qt.CursorShape.WaitCursor)
self.bytes_written = 0
self.bytes_received = 0
self,
"Loopback",
f"Unable to start the test {self.tcp_server.errorString()}",
- QMessageBox.Retry | QMessageBox.Cancel,
+ QMessageBox.StandardButton.Retry | QMessageBox.StandardButton.Cancel,
)
- if ret == QMessageBox.Cancel:
+ if ret == QMessageBox.StandardButton.Cancel:
return
self.server_status_label.setText("Listening")
self.client_status_label.setText("Connecting")
- self.tcp_client.connectToHost(QHostAddress.LocalHost, self.tcp_server.serverPort())
+ self.tcp_client.connectToHost(QHostAddress.SpecialAddress.LocalHost,
+ self.tcp_server.serverPort())
def accept_connection(self):
self.client_status_label.setText(f"Sent {self.bytes_written / (1024 ** 2)} MB")
def display_error(self, socket_error: QAbstractSocket.SocketError):
- if socket_error == QAbstractSocket.RemoteHostClosedError:
+ if socket_error == QAbstractSocket.SocketError.RemoteHostClosedError:
return
QMessageBox.information(
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
--- /dev/null
+Threaded Fortune Server Example
+===============================
+
+The Threaded Fortune Server example shows how to create a server for a simple
+network service that uses threads to handle requests from different clients.
+It corresponds to the Qt example `Threaded Fortune Server Example
+<https://doc.qt.io/qt-6/qtnetwork-threadedfortuneserver-example.html>`_.
+
+.. image:: threadedfortuneserver.png
+ :align: center
+ :alt: threadedfortuneserver screenshot
+ :width: 400
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the network/threadedfortuneserver example from Qt v5.x, originating from PyQt"""
return
block = QByteArray()
- outstr = QDataStream(block, QIODevice.WriteOnly)
- outstr.setVersion(QDataStream.Qt_4_0)
+ outstr = QDataStream(block, QIODevice.OpenModeFlag.WriteOnly)
+ outstr.setVersion(QDataStream.Version.Qt_4_0)
outstr.writeUInt16(0)
outstr.writeQString(self.text)
outstr.device().seek(0)
self.server = FortuneServer()
status_label = QLabel()
- status_label.setTextInteractionFlags(Qt.TextBrowserInteraction)
+ status_label.setTextInteractionFlags(Qt.TextInteractionFlag.TextBrowserInteraction)
status_label.setWordWrap(True)
quit_button = QPushButton("Quit")
quit_button.setAutoDefault(False)
return
for ip_address in QNetworkInterface.allAddresses():
- if ip_address != QHostAddress.LocalHost and ip_address.toIPv4Address() != 0:
+ if (ip_address != QHostAddress.SpecialAddress.LocalHost
+ and ip_address.toIPv4Address() != 0):
break
else:
- ip_address = QHostAddress(QHostAddress.LocalHost)
+ ip_address = QHostAddress(QHostAddress.SpecialAddress.LocalHost)
ip_address = ip_address.toString()
port = self.server.serverPort()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the networkauth redditclient example from Qt v6.x"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import functools
from PySide6.QtCore import (QAbstractTableModel, QJsonDocument, QModelIndex,
def data(self, index, role):
if not index.isValid():
return None
- if role == Qt.DisplayRole:
+ if role == Qt.ItemDataRole.DisplayRole:
children_object = self._threads[index.row()]
data_object = children_object["data"]
return data_object["title"]
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import functools
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the opengl/contextinfo example from Qt v5.x"""
def print_surface_format(surface_format):
- if surface_format.profile() == QSurfaceFormat.CoreProfile:
+ if surface_format.profile() == QSurfaceFormat.OpenGLContextProfile.CoreProfile:
profile_name = 'core'
else:
profile_name = 'compatibility'
class RenderWindow(QWindow):
def __init__(self, fmt):
super().__init__()
- self.setSurfaceType(QWindow.OpenGLSurface)
+ self.setSurfaceType(QWindow.SurfaceType.OpenGLSurface)
self.setFormat(fmt)
self.context = QOpenGLContext(self)
self.context.setFormat(self.requestedFormat())
self.vbo = QOpenGLBuffer()
fmt = self.context.format()
- use_new_style_shader = fmt.profile() == QSurfaceFormat.CoreProfile
+ use_new_style_shader = fmt.profile() == QSurfaceFormat.OpenGLContextProfile.CoreProfile
# Try to handle 3.0 & 3.1 that do not have the core/compatibility profile
# concept 3.2+ has. This may still fail since version 150 (3.2) is
# specified in the sources but it's worth a try.
- if (fmt.renderableType() == QSurfaceFormat.OpenGL and fmt.majorVersion() == 3
+ if (fmt.renderableType() == QSurfaceFormat.RenderableType.OpenGL and fmt.majorVersion() == 3
and fmt.minorVersion() <= 1):
- use_new_style_shader = not fmt.testOption(QSurfaceFormat.DeprecatedFunctions)
+ use_new_style_shader = not fmt.testOption(QSurfaceFormat.FormatOption.DeprecatedFunctions) # noqa: E501
vertex_shader = vertex_shader_source if use_new_style_shader else vertex_shader_source_110
fragment_shader = (fragment_shader_source
if use_new_style_shader
else fragment_shader_source_110)
- if not self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, vertex_shader):
+ if not self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Vertex, vertex_shader): # noqa: E501
log = self.program.log()
raise Exception("Vertex shader could not be added: {log} ({vertexShader})")
- if not self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, fragment_shader):
+ if not self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Fragment, fragment_shader): # noqa: E501
log = self.program.log()
raise Exception(f"Fragment shader could not be added: {log} ({fragment_shader})")
if not self.program.link():
help='Use Desktop OpenGL')
options = parser.parse_args()
if options.gles:
- QCoreApplication.setAttribute(Qt.AA_UseOpenGLES)
+ QCoreApplication.setAttribute(Qt.ApplicationAttribute.AA_UseOpenGLES)
elif options.software:
- QCoreApplication.setAttribute(Qt.AA_UseSoftwareOpenGL)
+ QCoreApplication.setAttribute(Qt.ApplicationAttribute.AA_UseSoftwareOpenGL)
elif options.desktop:
- QCoreApplication.setAttribute(Qt.AA_UseDesktopOpenGL)
+ QCoreApplication.setAttribute(Qt.ApplicationAttribute.AA_UseDesktopOpenGL)
app = QApplication(sys.argv)
main_window = MainWindow()
# Copyright (C) 2023 The Qt Company Ltd.
# Copyright (C) 2013 Riverbank Computing Limited.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import ctypes
from PySide6.QtCore import Signal, Slot, Qt, QSize, QPointF
QOpenGLWidget.__init__(self, parent)
QOpenGLFunctions.__init__(self)
- self._core = QSurfaceFormat.defaultFormat().profile() == QSurfaceFormat.CoreProfile
+ self._core = QSurfaceFormat.defaultFormat().profile() == QSurfaceFormat.OpenGLContextProfile.CoreProfile # noqa: E501
self._x_rot = 0
self._y_rot = 0
self._vertex_shader = VERTEX_SHADER_SOURCE
self._fragment_shader = FRAGMENT_SHADER_SOURCE
- self.program.addShaderFromSourceCode(QOpenGLShader.Vertex,
+ self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Vertex,
self._vertex_shader)
- self.program.addShaderFromSourceCode(QOpenGLShader.Fragment,
+ self.program.addShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Fragment,
self._fragment_shader)
self.program.bindAttributeLocation("vertex", 0)
self.program.bindAttributeLocation("normal", 1)
dx = pos.x() - self._last_pos.x()
dy = pos.y() - self._last_pos.y()
- if event.buttons() & Qt.LeftButton:
+ if event.buttons() & Qt.MouseButton.LeftButton:
self.set_xrotation(self._x_rot + 8 * dy)
self.set_yrotation(self._y_rot + 8 * dx)
elif event.buttons() & Qt.RightButton:
# Copyright (C) 2023 The Qt Company Ltd.
# Copyright (C) 2013 Riverbank Computing Limited.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import math
# Copyright (C) 2023 The Qt Company Ltd.
# Copyright (C) 2013 Riverbank Computing Limited.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the opengl/hellogl2 example from Qt v6.x"""
from glwidget import GLWidget
except ImportError:
app = QApplication(sys.argv)
- message_box = QMessageBox(QMessageBox.Critical, "OpenGL hellogl",
+ message_box = QMessageBox(QMessageBox.Icon.Critical, "OpenGL hellogl",
"PyOpenGL must be installed to run this example.",
- QMessageBox.Close)
+ QMessageBox.StandardButton.Close)
message_box.setDetailedText("Run:\npip install PyOpenGL PyOpenGL_accelerate")
message_box.exec()
sys.exit(1)
fmt.setSamples(4)
if options.coreprofile:
fmt.setVersion(3, 2)
- fmt.setProfile(QSurfaceFormat.CoreProfile)
+ fmt.setProfile(QSurfaceFormat.OpenGLContextProfile.CoreProfile)
QSurfaceFormat.setDefaultFormat(fmt)
GLWidget.set_transparent(options.transparent)
main_window = MainWindow()
if options.transparent:
- main_window.setAttribute(Qt.WA_TranslucentBackground)
- main_window.setAttribute(Qt.WA_NoSystemBackground, False)
+ main_window.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
+ main_window.setAttribute(Qt.WidgetAttribute.WA_NoSystemBackground, False)
main_window.show()
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Slot, Qt
from PySide6.QtGui import QKeySequence
def __init__(self):
super().__init__()
menuWindow = self.menuBar().addMenu("Window")
- menuWindow.addAction("Add new", QKeySequence(Qt.CTRL | Qt.Key_N),
+ menuWindow.addAction("Add new", QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_N),
self.onAddNew)
- menuWindow.addAction("Quit", QKeySequence(Qt.CTRL | Qt.Key_Q),
+ menuWindow.addAction("Quit", QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Q),
qApp.closeAllWindows) # noqa: F821
self.onAddNew()
# Copyright (C) 2023 The Qt Company Ltd.
# Copyright (C) 2013 Riverbank Computing Limited.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Slot, Qt
from PySide6.QtWidgets import (QHBoxLayout, QMainWindow,
self.setWindowTitle(self.tr("Hello GL"))
def create_slider(self):
- slider = QSlider(Qt.Vertical)
+ slider = QSlider(Qt.Orientation.Vertical)
slider.setRange(0, 360 * 16)
slider.setSingleStep(16)
slider.setPageStep(15 * 16)
slider.setTickInterval(15 * 16)
- slider.setTickPosition(QSlider.TicksRight)
+ slider.setTickPosition(QSlider.TickPosition.TicksRight)
return slider
def closeEvent(self, event):
event.accept()
def keyPressEvent(self, event):
- if self.isWindow() and event.key() == Qt.Key_Escape:
+ if self.isWindow() and event.key() == Qt.Key.Key_Escape:
self.close()
else:
super().keyPressEvent(event)
"Main window already occupied")
return
- self.setAttribute(Qt.WA_DeleteOnClose, False)
+ self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, False)
self._dock_btn.setText("Undock")
mainWindow.setCentralWidget(self)
def undock(self):
self.setParent(None)
- self.setAttribute(Qt.WA_DeleteOnClose)
+ self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
geometry = self.screen().availableGeometry()
x = geometry.x() + (geometry.width() - self.width()) / 2
y = geometry.y() + (geometry.height() - self.height()) / 2
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the opengl/textures example from Qt v6.x showing the use
of legacy OpenGL functions with QOpenGLVersionFunctionsFactory."""
def __init__(self, parent):
super().__init__(parent)
- self.clearColor = Qt.black
+ self.clearColor = Qt.GlobalColor.black
self.xRot = 0
self.yRot = 0
self.zRot = 0
def initializeGL(self):
profile = QOpenGLVersionProfile()
profile.setVersion(3, 2)
- profile.setProfile(QSurfaceFormat.CompatibilityProfile)
+ profile.setProfile(QSurfaceFormat.OpenGLContextProfile.CompatibilityProfile)
self.funcs = QOpenGLVersionFunctionsFactory.get(profile)
self.funcs.initializeOpenGLFunctions()
dx = pos.x() - self.lastPos.x()
dy = pos.y() - self.lastPos.y()
- if event.buttons() & Qt.LeftButton:
+ if event.buttons() & Qt.MouseButton.LeftButton:
self.rotateBy(8 * dy, 8 * dx, 0)
elif event.buttons() & Qt.RightButton:
self.rotateBy(8 * dy, 0, 8 * dx)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtOpenGLWidgets import QOpenGLWidget
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Threaded QOpenGLWidget Example from Qt v6.x"""
except ImportError:
app = QApplication(sys.argv)
message = "PyOpenGL must be installed to run this example."
- message_box = QMessageBox(QMessageBox.Critical,
+ message_box = QMessageBox(QMessageBox.Icon.Critical,
"Threaded QOpenGLWidget Example",
- message, QMessageBox.Close)
+ message, QMessageBox.StandardButton.Close)
detail = "Run:\npip install PyOpenGL PyOpenGL_accelerate"
message_box.setDetailedText(detail)
message_box.exec()
top_gl_widget.setToolTip(tool_tip)
print(tool_tip)
- close_shortcut = QShortcut(Qt.CTRL | Qt.Key_Q, top_gl_widget)
+ close_shortcut = QShortcut(Qt.Modifier.CTRL | Qt.Key.Key_Q, top_gl_widget)
close_shortcut.activated.connect(QApplication.closeAllWindows)
- close_shortcut.setContext(Qt.ApplicationShortcut)
+ close_shortcut.setContext(Qt.ShortcutContext.ApplicationShortcut)
mw1 = None
mw2 = None
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWidgets import QWidget, QHBoxLayout
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import ctypes
import math
QMetaObject.invokeMethod(self._glwidget, "update")
def _init_gl(self):
- vshader = QOpenGLShader(QOpenGLShader.Vertex, self)
+ vshader = QOpenGLShader(QOpenGLShader.ShaderTypeBit.Vertex, self)
vshader.compileSourceCode(VERTEX_SHADER)
- fshader = QOpenGLShader(QOpenGLShader.Fragment, self)
+ fshader = QOpenGLShader(QOpenGLShader.ShaderTypeBit.Fragment, self)
fshader.compileSourceCode(FRAGMENT_SHADER)
self._program.addShader(vshader)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from argparse import ArgumentParser, RawTextHelpFormatter
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import math
import sys
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<widget class="QTabWidget" name="tabWidget">
<property name="sizePolicy">
</sizepolicy>
</property>
<property name="tabPosition">
- <enum>QTabWidget::West</enum>
+ <enum>QTabWidget::TabPosition::West</enum>
</property>
<property name="currentIndex">
<number>0</number>
<x>0</x>
<y>0</y>
<width>700</width>
- <height>23</height>
+ <height>26</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
################################################################################
## Form generated from reading UI file 'mainwindow.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
if QIcon.hasThemeIcon(iconThemeName):
icon = QIcon.fromTheme(iconThemeName)
else:
- icon.addFile(u":/icons/images/document-open.svgz", QSize(), QIcon.Normal, QIcon.Off)
+ icon.addFile(u":/icons/images/document-open.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionOpen.setIcon(icon)
self.actionQuit = QAction(MainWindow)
if QIcon.hasThemeIcon(iconThemeName):
icon3 = QIcon.fromTheme(iconThemeName)
else:
- icon3.addFile(u":/icons/images/zoom-in.svgz", QSize(), QIcon.Normal, QIcon.Off)
+ icon3.addFile(u":/icons/images/zoom-in.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionZoom_In.setIcon(icon3)
self.actionZoom_Out = QAction(MainWindow)
if QIcon.hasThemeIcon(iconThemeName):
icon4 = QIcon.fromTheme(iconThemeName)
else:
- icon4.addFile(u":/icons/images/zoom-out.svgz", QSize(), QIcon.Normal, QIcon.Off)
+ icon4.addFile(u":/icons/images/zoom-out.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionZoom_Out.setIcon(icon4)
self.actionPrevious_Page = QAction(MainWindow)
if QIcon.hasThemeIcon(iconThemeName):
icon5 = QIcon.fromTheme(iconThemeName)
else:
- icon5.addFile(u":/icons/images/go-previous-view-page.svgz", QSize(), QIcon.Normal, QIcon.Off)
+ icon5.addFile(u":/icons/images/go-previous-view-page.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionPrevious_Page.setIcon(icon5)
self.actionNext_Page = QAction(MainWindow)
if QIcon.hasThemeIcon(iconThemeName):
icon6 = QIcon.fromTheme(iconThemeName)
else:
- icon6.addFile(u":/icons/images/go-next-view-page.svgz", QSize(), QIcon.Normal, QIcon.Off)
+ icon6.addFile(u":/icons/images/go-next-view-page.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionNext_Page.setIcon(icon6)
self.actionContinuous = QAction(MainWindow)
self.actionBack.setObjectName(u"actionBack")
self.actionBack.setEnabled(False)
icon7 = QIcon()
- icon7.addFile(u":/icons/images/go-previous-view.svgz", QSize(), QIcon.Normal, QIcon.Off)
+ icon7.addFile(u":/icons/images/go-previous-view.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionBack.setIcon(icon7)
self.actionForward = QAction(MainWindow)
self.actionForward.setObjectName(u"actionForward")
self.actionForward.setEnabled(False)
icon8 = QIcon()
- icon8.addFile(u":/icons/images/go-next-view.svgz", QSize(), QIcon.Normal, QIcon.Off)
+ icon8.addFile(u":/icons/images/go-next-view.svgz", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionForward.setIcon(icon8)
self.centralWidget = QWidget(MainWindow)
self.centralWidget.setObjectName(u"centralWidget")
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
self.splitter = QSplitter(self.widget)
self.splitter.setObjectName(u"splitter")
- self.splitter.setOrientation(Qt.Horizontal)
+ self.splitter.setOrientation(Qt.Orientation.Horizontal)
self.tabWidget = QTabWidget(self.splitter)
self.tabWidget.setObjectName(u"tabWidget")
sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.tabWidget.sizePolicy().hasHeightForWidth())
self.tabWidget.setSizePolicy(sizePolicy)
- self.tabWidget.setTabPosition(QTabWidget.West)
+ self.tabWidget.setTabPosition(QTabWidget.TabPosition.West)
self.tabWidget.setDocumentMode(False)
self.bookmarkTab = QWidget()
self.bookmarkTab.setObjectName(u"bookmarkTab")
MainWindow.setCentralWidget(self.centralWidget)
self.menuBar = QMenuBar(MainWindow)
self.menuBar.setObjectName(u"menuBar")
- self.menuBar.setGeometry(QRect(0, 0, 700, 23))
+ self.menuBar.setGeometry(QRect(0, 0, 700, 26))
self.menuFile = QMenu(self.menuBar)
self.menuFile.setObjectName(u"menuFile")
self.menuHelp = QMenu(self.menuBar)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtPdfWidgets import QPdfView
from PySide6.QtWidgets import QComboBox
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from pathlib import Path
engine = QQmlApplicationEngine()
qml_file = Path(__file__).parent / "main.qml"
engine.load(QUrl.fromLocalFile(qml_file))
-
if not engine.rootObjects():
sys.exit(-1)
- sys.exit(app.exec())
+
+ exit_code = app.exec()
+ del engine
+ sys.exit(exit_code)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QAbstractListModel, QByteArray, QModelIndex, Qt, Slot
@QmlElement
class BaseModel(QAbstractListModel):
- RatioRole = Qt.UserRole + 1
+ RatioRole = Qt.ItemDataRole.UserRole + 1
def __init__(self, parent=None):
super().__init__(parent=parent)
def roleNames(self):
default = super().roleNames()
default[self.RatioRole] = QByteArray(b"ratio")
- default[Qt.BackgroundRole] = QByteArray(b"backgroundColor")
+ default[Qt.ItemDataRole.BackgroundRole] = QByteArray(b"backgroundColor")
return default
def data(self, index, role: int):
ret = None
elif not index.isValid():
ret = None
- elif role == Qt.DisplayRole:
+ elif role == Qt.ItemDataRole.DisplayRole:
ret = self.db[index.row()]["text"]
- elif role == Qt.BackgroundRole:
+ elif role == Qt.ItemDataRole.BackgroundRole:
ret = self.db[index.row()]["bgColor"]
elif role == self.RatioRole:
ret = self.db[index.row()]["ratio"]
def setData(self, index, value, role):
if not index.isValid():
return False
- if role == Qt.EditRole:
+ if role == Qt.ItemDataRole.EditRole:
self.db[index.row()]["text"] = value
return True
+++ /dev/null
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import os
-from pathlib import Path
-import sys
-from PySide6.QtCore import QTimer, QUrl
-from PySide6.QtGui import QGuiApplication
-from PySide6.QtQuick import QQuickView
-
-if __name__ == '__main__':
- app = QGuiApplication(sys.argv)
-
- timer = QTimer()
- timer.start(2000)
-
- view = QQuickView()
- qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
- view.setSource(QUrl.fromLocalFile(qml_file))
- if view.status() == QQuickView.Error:
- sys.exit(-1)
- root = view.rootObject()
-
- timer.timeout.connect(root.updateRotater)
-
- view.show()
- res = app.exec()
- # Deleting the view before it goes out of scope is required to make sure all child QML instances
- # are destroyed in the correct order.
- del view
- sys.exit(res)
+++ /dev/null
-{
- "files": ["main.py", "view.qml"]
-}
+++ /dev/null
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-
-Rectangle {
- id: page
-
- function updateRotater() {
- rotater.angle = rotater.angle + 45
- }
-
- width: 500; height: 200
- color: "lightgray"
-
- Rectangle {
- id: rotater
- property real angle : 0
- x: 240
- width: 100; height: 10
- color: "black"
- y: 95
-
- transform: Rotation {
- origin.x: 10; origin.y: 5
- angle: rotater.angle
- Behavior on angle {
- SpringAnimation {
- spring: 1.4
- damping: .05
- }
- }
- }
- }
-
-}
+++ /dev/null
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import os
-from pathlib import Path
-import sys
-from PySide6.QtCore import QObject, QTimer, QUrl, Signal, Slot
-from PySide6.QtGui import QGuiApplication
-from PySide6.QtQuick import QQuickView
-from PySide6.QtQml import QmlElement
-
-
-# To be used on the @QmlElement decorator
-# (QML_IMPORT_MINOR_VERSION is optional)
-QML_IMPORT_NAME = "examples.signals.pytoqml2"
-QML_IMPORT_MAJOR_VERSION = 1
-
-
-@QmlElement
-class RotateValue(QObject):
- valueChanged = Signal(int, arguments=['val'])
-
- def __init__(self):
- super().__init__()
- self.r = 0
-
- @Slot()
- def increment(self):
- self.r = self.r + 10
- self.valueChanged.emit(self.r)
-
-
-if __name__ == '__main__':
- app = QGuiApplication(sys.argv)
- view = QQuickView()
-
- rotatevalue = RotateValue()
- timer = QTimer()
- timer.start(2000)
- view.setInitialProperties({"rotatevalue": rotatevalue})
-
- qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
- view.setSource(QUrl.fromLocalFile(qml_file))
- if view.status() == QQuickView.Error:
- sys.exit(-1)
-
- timer.timeout.connect(rotatevalue.increment)
-
- view.show()
- res = app.exec()
- # Deleting the view before it goes out of scope is required to make
- # sure all child QML instances are destroyed in the correct order.
- del view
- sys.exit(res)
+++ /dev/null
-{
- "files": ["main.py", "view.qml"]
-}
+++ /dev/null
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick
-import QtQml
-
-import examples.signals.pytoqml2 1.0
-
-Rectangle {
- id: page
-
- width: 500; height: 200
- color: "lightgray"
- required property RotateValue rotatevalue
-
- Text {
- id: helloText
- text: "Hello world!"
- anchors.horizontalCenter: page.horizontalCenter
- y: 30
- font.pointSize: 24; font.bold: true
- }
-
- Connections {
- target: rotatevalue
- function onValueChanged(val) {
- helloText.rotation = val
- }
- }
-}
--- /dev/null
+Calling Python Methods from QML
+===============================
+
+Introduce how to invoke Python methods (slots) from QML.
+
+**Key Features:**
+
+- **Python Class with Slots:** Defines a Console class in Python with multiple slots using the
+ :deco:`~PySide6.QtCore.Slot` decorator.
+- **Exposing Python Class to QML:** Uses :deco:`~PySide6.QtQml.QmlElement` to make the Console class
+ available in QML.
+- **Calling Slots from QML:** In QML, instantiates Console and calls its methods in response to user
+ interactions.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
from pathlib import Path
qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
view.setSource(QUrl.fromLocalFile(qml_file))
- if view.status() == QQuickView.Error:
+ if view.status() == QQuickView.Status.Error:
sys.exit(-1)
view.show()
res = app.exec()
--- /dev/null
+Receiving return values from Python in QML
+==========================================
+
+Demonstrate how to call Python methods from QML that return values.
+
+**Key Features:**
+
+- **Python Class with Returning Slot:** Defines a `RotateValue` class with a slot that returns an
+ integer.
+- **Exposing Class to QML:** Uses :deco:`~PySide6.QtQml.QmlElement` to expose RotateValue to QML.
+- **Using Return Values in QML:** Calls the Python method from QML and uses the returned value to
+ update the UI.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
from pathlib import Path
qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
view.setSource(QUrl.fromLocalFile(qml_file))
- if view.status() == QQuickView.Error:
+ if view.status() == QQuickView.Status.Error:
sys.exit(-1)
view.show()
res = app.exec()
--- /dev/null
+Handling QML Signals in Python
+==============================
+
+Show how to respond to signals emitted from QML in Python.
+
+**Key Features:**
+
+- **Defining Signals in QML:** Defines custom signals like `buttonClicked` and `textRotationChanged`
+- **Connecting Signals to Python Functions:** In Python, connects these signals to functions.
+- **Handling Signals in Python:** Implements the `sayThis` function to handle the signals.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
from pathlib import Path
view = QQuickView()
qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
view.setSource(QUrl.fromLocalFile(qml_file))
- if view.status() == QQuickView.Error:
+ if view.status() == QQuickView.Status.Error:
sys.exit(-1)
root = view.rootObject()
--- /dev/null
+Directly Connecting QML Component Signals to Python Functions
+=============================================================
+
+Teach how to connect signals of specific QML components to Python functions using object names.
+
+**Key Features:**
+
+- **Assigning `objectName` in QML:** Sets `objectName` properties to identify QML objects.
+- **Finding QML Objects in Python:** Uses `findChild` to get references to QML objects.
+- **Connecting Component Signals to Python Functions:** Connects signals directly to Python
+ functions.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
from pathlib import Path
view = QQuickView()
qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
view.setSource(QUrl.fromLocalFile(qml_file))
- if view.status() == QQuickView.Error:
+ if view.status() == QQuickView.Status.Error:
sys.exit(-1)
root = view.rootObject()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from pathlib import Path
if not engine.rootObjects():
sys.exit(-1)
- sys.exit(app.exec())
+ exit_code = app.exec()
+ del engine
+ sys.exit(exit_code)
-.. _qml-adding-types-example:
-
Extending QML - Adding Types Example
====================================
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the qml/examples/qml/referenceexamples/adding example from Qt v6.x"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property
from PySide6.QtQml import QmlElement
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property, Signal
from PySide6.QtQml import QmlElement, ListProperty
-.. _qml-advanced1-base-project:
-
Extending QML (advanced) - BirthdayParty Base Project
=====================================================
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the
qml/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project example from Qt v6.x"""
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property, Signal
from PySide6.QtQml import QmlElement
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property, Signal
from PySide6.QtQml import QmlElement, ListProperty
-.. _qml-advanced2-inheritance-and-coercion:
-
Extending QML (advanced) - Inheritance and Coercion
===================================================
The ``Person`` class remains unaltered and the ``Boy`` and ``Girl`` classes are
trivial extensions of it. The types and their QML name are registered with the
-QML engine with ``@QmlElement``.
+QML engine with :deco:`~PySide6.QtQml.QmlElement`.
Notice that the ``host`` and ``guests`` properties in ``BirthdayParty`` still
take instances of ``Person``.
While we want to disallow instantiating ``Person`` from within QML, it still
needs to be registered with the QML engine so that it can be used as a property
-type and other types can be coerced to it. This is what the ``@QmlUncreatable``
-macro does. As all three types, ``Person``, ``Boy`` and ``Girl``, have been
+type and other types can be coerced to it. This is what
+:deco:`~PySide6.QtQml.QmlUncreatable` does.
+As all three types, ``Person``, ``Boy`` and ``Girl``, have been
registered with the QML system, on assignment, QML automatically (and
type-safely) converts the ``Boy`` and ``Girl`` objects into a ``Person``.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the
qml/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion example
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property, Signal
from PySide6.QtQml import QmlElement, QmlUncreatable
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, ClassInfo, Property, Signal
from PySide6.QtQml import QmlElement, ListProperty
-.. _qml-advanced3-default-properties:
-
Extending QML (advanced) - Default Properties
=============================================
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the
qml/examples/qml/tutorials/extending-qml-advanced/default advanced3-Default-properties example
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property, Signal
from PySide6.QtQml import QmlAnonymous, QmlElement
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, ClassInfo, Property, Signal
from PySide6.QtQml import QmlElement, ListProperty
-.. _qml-advanced-advanced4-grouped-properties:
-
Extending QML (advanced) - Grouped Properties
=============================================
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the
qml/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties example
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property, Signal
from PySide6.QtGui import QColor
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QDate, QObject, ClassInfo, Property, Signal
from PySide6.QtQml import QmlAnonymous, QmlAttached, QmlElement, ListProperty
-.. _qml-advanced-advanced5-attached-properties:
-
Extending QML (advanced) - Attached Properties
==============================================
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the
qml/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties example
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property, Signal
from PySide6.QtQml import QmlAnonymous, QmlElement
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QDate, QObject, ClassInfo, Property, QTime, Signal
from PySide6.QtQml import QmlAnonymous, QmlAttached, QmlElement, ListProperty
-.. _qml-advanced/advanced6-property-value-source:
-
Extending QML (advanced) - Property Value Source
================================================
:lines: 13-49
The class ``HappyBirthdaySong`` is added as a value source. It must inherit
-from ``QQmlPropertyValueSource`` and implement its interface. The
+from :class:`~PySide6.QtQml.QQmlPropertyValueSource` and implement its interface. The
``setTarget()`` function is used to define which property this source acts
upon. In this case, the value source writes to the ``announcement`` property of
the ``BirthdayParty`` to display the lyrics over time. It has an internal timer
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QTimer, Property, Signal, Slot
from PySide6.QtQml import QmlElement, QPyQmlPropertyValueSource
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the
qml/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source example
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property, Signal
from PySide6.QtQml import QmlAnonymous, QmlElement
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QDate, QObject, ClassInfo, Property, QTime, Signal
from PySide6.QtQml import QmlAnonymous, QmlAttached, QmlElement, ListProperty
-.. _qml-binding-example:
-
Extending QML - Binding Example
===============================
-This example builds on the :ref:`qml-adding-types-example`,
-the :ref:`qml-attached-properties-example`,
-the :ref:`qml-default-property-example`,
-the :ref:`qml-inheritance-and-coercion-example`
-the :ref:`qml-object-and-list-property-types-example`
-and the :ref:`qml-valuesource-example`.
+This example builds on the :ref:`example_qml_tutorials_extending-qml-advanced_adding`,
+the :ref:`example_qml_tutorials_extending-qml-advanced_advanced5-attached-properties`,
+the :ref:`example_qml_tutorials_extending-qml-advanced_advanced3-default-properties`,
+the :ref:`example_qml_tutorials_extending-qml-advanced_advanced2-inheritance-and-coercion`
+the :ref:`example_qml_tutorials_extending-qml-advanced_properties`
+and the :ref:`example_qml_tutorials_extending-qml-advanced_advanced6-property-value-source`.
Running the Example
-------------------
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QTimer, Property, Slot
from PySide6.QtQml import QmlElement, QPyQmlPropertyValueSource
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the qml/examples/qml/referenceexamples/binding example from Qt v6.x"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property, Signal
from PySide6.QtQml import QmlAnonymous, QmlElement
-.. _qml-extension-objects-example:
-
Extending QML - Extension Objects Example
=========================================
-This example builds on the the :ref:`qml-adding-types-example`.
+This example builds on the the :ref:`example_qml_tutorials_extending-qml-advanced_adding`.
-Shows how to use QmlExtended decorator to provide an extension object to a
+Shows how to use :deco:`~PySide6.QtQml.QmlExtended` to provide an extension object to a
QLineEdit without modifying or subclassing it.
Firstly, the LineEditExtension class is registered with the QML system as an
-extension of QLineEdit. We declare a foreign type to do this as we cannot
-modify Qt's internal QLineEdit class.
+extension of :class:`~PySide6.QtWidgets.QLineEdit`. We declare a foreign type to do
+this as we cannot modify Qt's internal QLineEdit class.
.. code-block:: python
class LineEditForeign(QObject):
-Note the usage of ``QmlNamedElement()`` instead of ``QmlElement()``.
+Note the usage of :deco:`~PySide6.QtQml.QmlNamedElement` instead of
+:deco:`~PySide6.QtQml.QmlElement`.
``QmlElement()`` uses the name of the containing type by default,
``LineEditExtension`` in this case. As the class being an extension class is
an implementation detail, we choose the more natural name ``QLineEdit``
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the qml/examples/qml/referenceexamples/extended example from Qt v6.x"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property, Slot
from PySide6.QtQml import QmlElement, ListProperty
-.. _qml-methods-example:
-
Extending QML - Methods Example
===============================
-This example builds on the :ref:`qml-adding-types-example`,
-the :ref:`qml-object-and-list-property-types-example` and
-the :ref:`qml-inheritance-and-coercion-example`.
+This example builds on the :ref:`example_qml_tutorials_extending-qml-advanced_adding`,
+the :ref:`example_qml_tutorials_extending-qml-advanced_properties` and
+the :ref:`example_qml_tutorials_extending-qml-advanced_advanced2-inheritance-and-coercion`.
The Methods Example has an additional method in the ``BirthdayParty`` class:
-``invite()``. ``invite()`` is decorated with ``@Slot`` so that it can be
+``invite()``. ``invite()`` is decorated with :deco:`~PySide6.QtCore.Slot` so that it can be
called from QML.
In ``example.qml``, the ``invite()`` method is called
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the qml/examples/qml/referenceexamples/methods example from Qt v6.x"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property
from PySide6.QtQml import QmlElement
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property
from PySide6.QtQml import QmlElement, ListProperty
-.. _qml-object-and-list-property-types-example:
-
Extending QML - Object and List Property Types Example
======================================================
Exporting C++ Properties.
-This example builds on :ref:`qml-adding-types-example`.
+This example builds on :ref:`example_qml_tutorials_extending-qml-advanced_adding`.
The Object and List Property Types example shows how to add object and list
properties in QML. This example adds a BirthdayParty type that specifies a
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the qml/examples/qml/referenceexamples/properties example from Qt v6.x"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property
from PySide6.QtQml import QmlElement
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the qml/tutorials/extending-qml/chapter1-basics example from Qt v5.x"""
def paint(self, painter):
pen = QPen(self.color, 2)
painter.setPen(pen)
- painter.setRenderHints(QPainter.Antialiasing, True)
+ painter.setRenderHints(QPainter.RenderHint.Antialiasing, True)
painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16)
@Property(QColor, final=True)
app = QGuiApplication(sys.argv)
view = QQuickView()
- view.setResizeMode(QQuickView.SizeRootObjectToView)
+ view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
qml_file = os.fspath(Path(__file__).resolve().parent / 'app.qml')
view.setSource(QUrl.fromLocalFile(qml_file))
- if view.status() == QQuickView.Error:
+ if view.status() == QQuickView.Status.Error:
sys.exit(-1)
view.show()
res = app.exec()
-.. _qml-chapter1-basics:
-
Extending QML - Creating a New Type
===================================
its two properties. Since QML makes extensive use of Qt's Meta-Object System
this new class must:
-* Inherit from ``QObject``
-* Declare its properties using the ``Property`` decorator
+* Inherit from :class:`~PySide6.QtCore.QObject`
+* Declare its properties using the :class:`~PySide6.QtCore.Property` decorator
Class Implementation
--------------------
:lineno-start: 21
:lines: 21-51
-The class inherits from ``QQuickPaintedItem`` because we want to override
-``QQuickPaintedItem.paint()`` to perform drawing operations with the
-``QPainter`` API. If the class just represented some data type and was not an
+The class inherits from :class:`~PySide6.QtQuick.QQuickPaintedItem`
+because we want to override :meth:`~PySide6.QtQuick.QQuickPaintedItem.paint`
+to perform drawing operations with the :class:`~PySide6.QtGui.QPainter` API.
+If the class just represented some data type and was not an
item that actually needed to be displayed, it could simply inherit from
-``QObject``. Or, if we want to extend the functionality of an existing
+:class:`~PySide6.QtCore.QObject`. Or, if we want to extend the functionality of an existing
``QObject``-based class, it could inherit from that class instead.
Alternatively, if we want to create a visual item that doesn't need to perform
drawing operations with the ``QPainter`` API, we can just subclass
-``QQuickItem``.
+:class:`~PySide6.QtQuick.QQuickItem`.
The ``PieChart`` class defines the two properties, ``name`` and ``color``, with
the ``Property`` decorator, and overrides ``QQuickPaintedItem.paint()``. The
-``PieChart`` class is registered using the ``QmlElement`` decorator, to allow
-it to be used from QML. If you don't register the class, ``app.qml`` won't be
-able to create a ``PieChart``.
+``PieChart`` class is registered using the :deco:`~PySide6.QtQml.QmlElement`
+decorator, to allow it to be used from QML. If you don't register the class, ``app.qml``
+won't be able to create a ``PieChart``.
QML Usage
---------
:lines: 7-26
Notice that although the color is specified as a string in QML, it is
-automatically converted to a ``QColor`` object for the PieChart ``color``
-property. Automatic conversions are provided for various other QML value types.
+automatically converted to a :class:`~PySide6.QtGui.QColor` object for the PieChart
+``color`` property. Automatic conversions are provided for various other QML value types.
For example, a string like "640x480" can be automatically converted to a
``QSize`` value.
-We'll also create a main function that uses a ``QQuickView`` to run and display
-``app.qml``. Here is the application ``basics.py``:
+We'll also create a main function that uses a :class:`~PySide6.QtQuick.QQuickView`
+to run and display ``app.qml``. Here is the application ``basics.py``:
.. literalinclude:: basics.py
:lineno-start: 54
-.. _qml-chapter2-methods:
-
Extending QML - Connecting to C++ Methods and Signals
=====================================================
:lineno-start: 54
:lines: 54-58
-The use of the ``Slot`` decorator makes the ``clearChart()`` method available
+The use of :deco:`~PySide6.QtCore.Slot` makes the ``clearChart()`` method available
to the Qt Meta-Object system, and in turn, to QML. The method simply changes
the color to ``Qt::transparent``, repaints the chart, then emits the
``chartCleared()`` signal:
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the qml/tutorials/extending-qml/chapter2-methods example from Qt v5.x"""
def paint(self, painter):
pen = QPen(self.color, 2)
painter.setPen(pen)
- painter.setRenderHints(QPainter.Antialiasing, True)
+ painter.setRenderHints(QPainter.RenderHint.Antialiasing, True)
painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16)
@Property(QColor, final=True)
app = QGuiApplication(sys.argv)
view = QQuickView()
- view.setResizeMode(QQuickView.SizeRootObjectToView)
+ view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
qml_file = os.fspath(Path(__file__).resolve().parent / 'app.qml')
view.setSource(QUrl.fromLocalFile(qml_file))
- if view.status() == QQuickView.Error:
+ if view.status() == QQuickView.Status.Error:
sys.exit(-1)
view.show()
res = app.exec()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the qml/tutorials/extending-qml/chapter3-bindings example from Qt v5.x"""
def paint(self, painter):
pen = QPen(self._color, 2)
painter.setPen(pen)
- painter.setRenderHints(QPainter.Antialiasing, True)
+ painter.setRenderHints(QPainter.RenderHint.Antialiasing, True)
painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16)
@Property(QColor, notify=colorChanged, final=True)
app = QGuiApplication(sys.argv)
view = QQuickView()
- view.setResizeMode(QQuickView.SizeRootObjectToView)
+ view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
qml_file = os.fspath(Path(__file__).resolve().parent / 'app.qml')
view.setSource(QUrl.fromLocalFile(qml_file))
- if view.status() == QQuickView.Error:
+ if view.status() == QQuickView.Status.Error:
sys.exit(-1)
view.show()
res = app.exec()
-.. _qml-chapter3-bindings:
-
Extending QML - Adding Property Bindings
========================================
``chartA`` , thereby changing both charts to the color blue.
It's easy to enable property binding for the ``color`` property. We add a
-``notify`` parameter to its ``Property`` decorator to indicate that a
-``colorChanged`` signal is emitted whenever the value changes.
+``notify`` parameter to its :class:`~PySide6.QtCore.Property` decorator to
+indicate that a ``colorChanged`` signal is emitted whenever the value changes.
.. literalinclude:: bindings.py
:lineno-start: 39
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the qml/tutorials/extending-qml/chapter4-customPropertyTypes example
from Qt v5.x"""
def paint(self, painter):
pen = QPen(self._color, 2)
painter.setPen(pen)
- painter.setRenderHints(QPainter.Antialiasing, True)
+ painter.setRenderHints(QPainter.RenderHint.Antialiasing, True)
painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16)
app = QGuiApplication(sys.argv)
view = QQuickView()
- view.setResizeMode(QQuickView.SizeRootObjectToView)
+ view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
qml_file = os.fspath(Path(__file__).resolve().parent / 'app.qml')
view.setSource(QUrl.fromLocalFile(qml_file))
- if view.status() == QQuickView.Error:
+ if view.status() == QQuickView.Status.Error:
sys.exit(-1)
view.show()
res = app.exec()
-.. _qml-chapter4-custompropertytypes:
-
Extending QML - Using Custom Property Types
===========================================
}
Aside from ``int``, we could use various other property types. Many of the Qt
-data types such as ``QColor``, ``QSize`` and ``QRect`` are automatically
+data types such as :class:`~PySide6.QtGui.QColor`, :class:`~PySide6.QtCore.QSize`
+and :class:`~PySide6.QtCore.QRect` are automatically
supported from QML.
If we want to create a property whose type is not supported by QML by default,
:lines: 4-22
Like ``PieChart``, this new ``PieSlice`` type inherits from
-``QQuickPaintedItem``, is exposed via the ``QmlElement`` decorator and declares
-its properties with the ``Property`` decorator:
+``QQuickPaintedItem``, is exposed via :deco:`~PySide6.QtQml.QmlElement` and declares
+its properties with the :class:`~PySide6.QtCore.Property` decorator:
.. literalinclude:: customPropertyTypes.py
:lineno-start: 21
There is one thing to be aware of when implementing ``setPieSlice()``. The
``PieSlice`` is a visual item, so it must be set as a child of the ``PieChart``
-using ``QQuickItem.setParentItem()`` so that the ``PieChart`` knows to paint
+using :meth:`~PySide6.QtQuick.QQuickItem.setParentItem` so that the ``PieChart`` knows to paint
this child item when its contents are drawn.
As with ``PieChart``, we add the ``Charts`` type namespace, version 1.0:
-.. _qml-chapter5-listproperties:
-
Extending QML - Using List Property Types
=========================================
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the qml/tutorials/extending-qml/chapter5-listproperties example from Qt v5.x"""
def paint(self, painter):
pen = QPen(self._color, 2)
painter.setPen(pen)
- painter.setRenderHints(QPainter.Antialiasing, True)
+ painter.setRenderHints(QPainter.RenderHint.Antialiasing, True)
painter.drawPie(
self.boundingRect().adjusted(1, 1, -1, -1), self._fromAngle * 16, self._angleSpan * 16)
app = QGuiApplication(sys.argv)
view = QQuickView()
- view.setResizeMode(QQuickView.SizeRootObjectToView)
+ view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
qml_file = os.fspath(Path(__file__).resolve().parent / 'app.qml')
view.setSource(QUrl.fromLocalFile(qml_file))
- if view.status() == QQuickView.Error:
+ if view.status() == QQuickView.Status.Error:
sys.exit(-1)
view.show()
res = app.exec()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Property
from PySide6.QtQml import QmlElement, ListProperty
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Property, QRectF
from PySide6.QtGui import QColor, QPainter, QPen
-.. _qml-chapter6-plugins-example:
-
Extending QML - Plugins Example
===============================
This example refers to the Python version of using a QML plugin in Python. The
idea of plugins in Python is non-existent because Python modules are
dynamically loaded anyway. We use this idea and our QML type registration
-decorators - ``QmlELement``/``QmlNamedElement`` - to register the QML modules as they
-are imported. The ``pyside6-qml`` tool does this for you by simply pointing to the
-``.qml`` file.
+decorators - :deco:`~PySide6.QtQml.QmlElement` / :deco:`~PySide6.QtQml.QmlNamedElement` -
+to register the QML modules as they are imported.
+The :ref:`pyside6-qml` tool does this for you by simply pointing to the ``.qml`` file.
.. image:: plugins.png
:width: 400
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
from dataclasses import dataclass
@QmlElement
@QmlSingleton
class PersonModel (QAbstractListModel):
- MyRole = Qt.UserRole + 1
+ MyRole = Qt.ItemDataRole.UserRole + 1
def __init__(self, data, parent=None):
super().__init__(parent)
def roleNames(self):
roles = {
PersonModel.MyRole: QByteArray(b'myrole'),
- Qt.DisplayRole: QByteArray(b'display')
+ Qt.ItemDataRole.DisplayRole: QByteArray(b'display')
}
return roles
def data(self, index, role):
d = self._data[index.row()]
- if role == Qt.DisplayRole:
+ if role == Qt.ItemDataRole.DisplayRole:
return d.name
- if role == Qt.DecorationRole:
- return Qt.black
+ if role == Qt.ItemDataRole.DecorationRole:
+ return Qt.GlobalColor.black
if role == PersonModel.MyRole:
return d.myrole
return None
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
view = QQuickView()
- view.setResizeMode(QQuickView.SizeRootObjectToView)
+ view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
view.setSource(QUrl.fromLocalFile(qml_file))
- if view.status() == QQuickView.Error:
+ if view.status() == QQuickView.Status.Error:
sys.exit(-1)
view.show()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from pathlib import Path
import sys
DataObject("Item 4", "yellow")]
view = QQuickView()
- view.setResizeMode(QQuickView.SizeRootObjectToView)
+ view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
view.setInitialProperties({"model": dataList})
qml_file = Path(__file__).parent / "view.qml"
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from pathlib import Path
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from argparse import ArgumentParser, RawTextHelpFormatter
from pathlib import Path
brush = QBrush(QColor("#007430"))
painter.setBrush(brush)
- painter.setPen(Qt.NoPen)
- painter.setRenderHint(QPainter.Antialiasing)
+ painter.setPen(Qt.PenStyle.NoPen)
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
itemSize = self.size()
QQmlDebuggingEnabler.enableDebugging(True)
app = QApplication(sys.argv)
view = QQuickView()
- view.setResizeMode(QQuickView.SizeRootObjectToView)
+ view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
qml_file = Path(__file__).parent / "main.qml"
view.setSource(QUrl.fromLocalFile(qml_file))
- if view.status() == QQuickView.Error:
+ if view.status() == QQuickView.Status.Error:
sys.exit(-1)
view.show()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import ctypes
import numpy
w.height() * w.devicePixelRatio())
self.m_program = QOpenGLShaderProgram()
- self.m_program.addCacheableShaderFromSourceCode(QOpenGLShader.Vertex,
+ self.m_program.addCacheableShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Vertex,
VERTEXSHADER_SOURCE)
- self.m_program.addCacheableShaderFromSourceCode(QOpenGLShader.Fragment,
+ self.m_program.addCacheableShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Fragment,
FRAGMENTSHADER_SOURCE)
self.m_program.bindAttributeLocation("vertex", 0)
self.m_program.bindAttributeLocation("coord", 1)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtGui import QGuiApplication
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
# only functional when Qt Quick is also using OpenGL
- QQuickWindow.setGraphicsApi(QSGRendererInterface.OpenGLRhi)
+ QQuickWindow.setGraphicsApi(QSGRendererInterface.GraphicsApi.OpenGLRhi)
window = WindowSingleThreaded()
window.resize(1024, 768)
window.show()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import numpy
from pathlib import Path
self.m_quickReady = False
self.m_dpr = 0
self.m_status_conn_id = None
- self.setSurfaceType(QSurface.OpenGLSurface)
+ self.setSurfaceType(QSurface.SurfaceType.OpenGLSurface)
format = QSurfaceFormat()
# Qt Quick may need a depth and stencil buffer. Always make sure these
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from pathlib import Path
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
- QQuickWindow.setGraphicsApi(QSGRendererInterface.OpenGL)
+ QQuickWindow.setGraphicsApi(QSGRendererInterface.GraphicsApi.OpenGL)
view = QQuickView()
- view.setResizeMode(QQuickView.SizeRootObjectToView)
+ view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
qml_file = Path(__file__).parent / "main.qml"
view.setSource(QUrl.fromLocalFile(qml_file))
- if view.status() == QQuickView.Error:
+ if view.status() == QQuickView.Status.Error:
sys.exit(-1)
view.show()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Property, QRunnable, Qt, Signal, Slot
from PySide6.QtQml import QmlElement
@Slot(QQuickWindow)
def handleWindowChanged(self, win):
if win:
- win.beforeSynchronizing.connect(self.sync, type=Qt.DirectConnection)
- win.sceneGraphInvalidated.connect(self.cleanup, type=Qt.DirectConnection)
- win.setColor(Qt.black)
+ win.beforeSynchronizing.connect(self.sync, type=Qt.ConnectionType.DirectConnection)
+ win.sceneGraphInvalidated.connect(self.cleanup, type=Qt.ConnectionType.DirectConnection)
+ win.setColor(Qt.GlobalColor.black)
self.sync()
@Slot()
window = self.window()
if not self._renderer:
self._renderer = SquircleRenderer()
- window.beforeRendering.connect(self._renderer.init, Qt.DirectConnection)
+ window.beforeRendering.connect(self._renderer.init, Qt.ConnectionType.DirectConnection)
window.beforeRenderPassRecording.connect(
- self._renderer.paint, Qt.DirectConnection
+ self._renderer.paint, Qt.ConnectionType.DirectConnection
)
self._renderer.setViewportSize(window.size() * window.devicePixelRatio())
self._renderer.setT(self._t)
def releaseResources(self):
self.window().scheduleRenderJob(
- CleanupJob(self._renderer), QQuickWindow.BeforeSynchronizingStage
+ CleanupJob(self._renderer), QQuickWindow.RenderStage.BeforeSynchronizingStage
)
self._renderer = None
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from textwrap import dedent
def init(self):
if not self._program:
rif = self._window.rendererInterface()
- assert (rif.graphicsApi() == QSGRendererInterface.OpenGL)
+ assert (rif.graphicsApi() == QSGRendererInterface.GraphicsApi.OpenGL)
self.initializeOpenGLFunctions()
self._program = QOpenGLShaderProgram()
- self._program.addCacheableShaderFromSourceCode(QOpenGLShader.Vertex, VERTEX_SHADER)
- self._program.addCacheableShaderFromSourceCode(QOpenGLShader.Fragment, FRAGMENT_SHADER)
+ self._program.addCacheableShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Vertex,
+ VERTEX_SHADER)
+ self._program.addCacheableShaderFromSourceCode(QOpenGLShader.ShaderTypeBit.Fragment,
+ FRAGMENT_SHADER)
self._program.bindAttributeLocation("vertices", 0)
self._program.link()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Qt Quick customgeometry example from Qt v6.x"""
self._node = None
self._geometry = None
- self.setFlag(QQuickItem.Flags.ItemHasContents, True)
+ self.setFlag(QQuickItem.Flag.ItemHasContents, True)
def p1(self):
return self._p1
self._node = QSGGeometryNode()
self._node.setGeometry(self._geometry)
- self._node.setFlag(QSGNode.Flags.OwnsGeometry)
+ self._node.setFlag(QSGNode.Flag.OwnsGeometry)
self._material = QSGFlatColorMaterial()
self._material.setColor(QColor(255, 0, 0))
self._node.setMaterial(self._material)
- self._node.setFlag(QSGNode.Flags.OwnsMaterial)
+ self._node.setFlag(QSGNode.Flag.OwnsMaterial)
else:
self._geometry = self._node.geometry()
self._geometry.allocate(self._segmentCount)
self._geometry.setVertexDataAsPoint2D(vertices)
- self._node.markDirty(QSGNode.DirtyGeometry)
+ self._node.markDirty(QSGNode.DirtyStateBit.DirtyGeometry)
return self._node
p1 = Property(QPointF, p1, setP1, notify=p1Changed)
fetchImages("json");
}
}
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
from pathlib import Path
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import random
self.setPrimitiveType(QQuick3DGeometry.PrimitiveType.Points)
self.addAttribute(
- QQuick3DGeometry.Attribute.PositionSemantic, 0, QQuick3DGeometry.Attribute.F32Type
+ QQuick3DGeometry.Attribute.Semantic.PositionSemantic, 0,
+ QQuick3DGeometry.Attribute.ComponentType.F32Type
)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import numpy as np
from PySide6.QtCore import Property, Signal
self.setBounds(QVector3D(-1.0, -1.0, 0.0), QVector3D(+1.0, +1.0, 0.0))
self.setPrimitiveType(QQuick3DGeometry.PrimitiveType.Triangles)
self.addAttribute(
- QQuick3DGeometry.Attribute.PositionSemantic, 0, QQuick3DGeometry.Attribute.F32Type
+ QQuick3DGeometry.Attribute.Semantic.PositionSemantic, 0,
+ QQuick3DGeometry.Attribute.ComponentType.F32Type
)
if self._hasNormals:
self.addAttribute(
- QQuick3DGeometry.Attribute.NormalSemantic,
+ QQuick3DGeometry.Attribute.Semantic.NormalSemantic,
3 * FLOAT_SIZE,
- QQuick3DGeometry.Attribute.F32Type,
+ QQuick3DGeometry.Attribute.ComponentType.F32Type,
)
if self._hasUV:
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
if not engine.rootObjects():
sys.exit(-1)
- sys.exit(app.exec())
+ exit_code = app.exec()
+ del engine
+ sys.exit(exit_code)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
import sys
if not engine.rootObjects():
sys.exit(-1)
- sys.exit(app.exec())
+ exit_code = app.exec()
+ del engine
+ sys.exit(exit_code)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Signal, Property, QSize
from PySide6.QtGui import QColor
def updateTexture(self):
self.setSize(QSize(self._width, self._height))
- self.setFormat(QQuick3DTextureData.RGBA8)
+ self.setFormat(QQuick3DTextureData.Format.RGBA8)
self.setHasTransparency(False)
self.setTextureData(self.generate_texture())
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
if not engine.rootObjects():
sys.exit(-1)
- ex = app.exec()
+ exit_code = app.exec()
del engine
-
- sys.exit(ex)
+ sys.exit(exit_code)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import bisect
from dataclasses import dataclass
@QEnum
class ContactRole(IntEnum):
- FullNameRole = Qt.DisplayRole
- AddressRole = Qt.UserRole
- CityRole = Qt.UserRole + 1
- NumberRole = Qt.UserRole + 2
+ FullNameRole = Qt.ItemDataRole.DisplayRole
+ AddressRole = Qt.ItemDataRole.UserRole
+ CityRole = Qt.ItemDataRole.UserRole + 1
+ NumberRole = Qt.ItemDataRole.UserRole + 2
@dataclass
class Contact:
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""
PySide6 port of Qt Quick Controls Contact List example from Qt v6.x
if not engine.rootObjects():
sys.exit(-1)
- ex = app.exec()
+ exit_code = app.exec()
del engine
- sys.exit(ex)
+ sys.exit(exit_code)
-.. _filesystemexplorer_example:
-
Qt Quick Controls - Filesystem Explorer
=======================================
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWidgets import QFileSystemModel
from PySide6.QtQuick import QQuickTextDocument
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""
This example shows how to customize Qt Quick Controls by implementing a simple filesystem explorer.
fsm = engine.singletonInstance("FileSystemModule", "FileSystemModel")
fsm.setInitialDirectory(args[0])
- sys.exit(app.exec())
+ exit_code = app.exec()
+ del engine
+ sys.exit(exit_code)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""
The gallery example is a simple application with a drawer menu that contains
window = rootObjects[0]
window.setIcon(QIcon(':/qt-project.org/logos/pysidelogo.png'))
- sys.exit(app.exec())
+ exit_code = app.exec()
+ del engine
+ sys.exit(exit_code)
--- /dev/null
+Model-View Server Example
+=========================
+
+Developing a simple server program that displays and makes changes to a
+QTreeView which is made available on a Remote Objects network. It
+corresponds to the Qt example `Model-View Server Example
+<https://doc.qt.io/qt-6/qtremoteobjects-modelviewserver-example.html>`_.
+
+.. image:: modelview.png
+ :align: center
+ :alt: modelview screenshot
+ :width: 400
# Copyright (C) 2017 Ford Motor Company
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the remoteobjects/modelviewclient example from Qt v5.x"""
# Copyright (C) 2017 Ford Motor Company
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the remoteobjects/modelviewserver example from Qt v5.x"""
def change_data(self):
for i in range(10, 50):
self._model.setData(self._model.index(i, 1),
- QColor(Qt.blue), Qt.BackgroundRole)
+ QColor(Qt.GlobalColor.blue),
+ Qt.ItemDataRole.BackgroundRole)
def insert_data(self):
self._model.insertRows(2, 9)
for i in range(2, 11):
self._model.setData(self._model.index(i, 1),
- QColor(Qt.green), Qt.BackgroundRole)
+ QColor(Qt.GlobalColor.green),
+ Qt.ItemDataRole.BackgroundRole)
self._model.setData(self._model.index(i, 1),
- "InsertedRow", Qt.DisplayRole)
+ "InsertedRow", Qt.ItemDataRole.DisplayRole)
def remove_data(self):
self._model.removeRows(2, 4)
item = self._model.item(0, 0)
item.setEnabled(False)
item = item.child(0, 0)
- item.setFlags(item.flags() & Qt.ItemIsSelectable)
+ item.setFlags(item.flags() & Qt.ItemFlag.ItemIsSelectable)
def move_data(self):
self._model.moveRows(QModelIndex(), 2, 4, QModelIndex(), 10)
first_item.appendRow(add_child(2, 2))
second_item = QStandardItem(f"FancyRow2TextNumber {i}")
if i % 2 == 0:
- first_item.setBackground(Qt.red)
+ first_item.setBackground(Qt.GlobalColor.red)
row = [first_item, second_item]
source_model.invisibleRootItem().appendRow(row)
data_list.append(f"FancyTextNumber {i}")
# Needed by QMLModelViewClient
role_names = {
- Qt.DisplayRole: QByteArray(b'_text'),
- Qt.BackgroundRole: QByteArray(b'_color')
+ Qt.ItemDataRole.DisplayRole: QByteArray(b'_text'),
+ Qt.ItemDataRole.BackgroundRole: QByteArray(b'_color')
}
source_model.setItemRoleNames(role_names)
- roles = [Qt.DisplayRole, Qt.BackgroundRole]
+ roles = [Qt.ItemDataRole.DisplayRole, Qt.ItemDataRole.BackgroundRole]
print("Creating registry host")
node = QRemoteObjectRegistryHost(QUrl("local:registry"))
"../main.py",
"../truck.cpp",
"../truck.h",
- "../CMakeLists.txt"]
+ "../CMakeLists.txt",
+ "../../utils/pyside_config.py"]
}
then compiled and linked into a shared library. The shared library is a
CPython extension module, which is loaded by the Python interpreter.
-Beacuse the C++ language has different semantics to Python, shiboken
+Because the C++ language has different semantics to Python, shiboken
needs help in figuring out how to generate the bindings code. This is
done by specifying a special XML file called a typesystem file.
To build:
.. code-block:: bash
+
ninja
ninja install
cd ..
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""An example showcasing how to use bindings for a custom non-Qt C++ library"""
{
"files": ["../main.cpp",
+ "../scriptableapplication.xml",
"../mainwindow.cpp",
"../mainwindow.h",
"../pythonutils.cpp",
"../pythonutils.h",
"../wrappedclasses.h",
- "../CMakeLists.txt"]
+ "../CMakeLists.txt",
+ "../../utils/pyside_config.py"]
}
#include "pythonutils.h"
#include <QtWidgets/QApplication>
+#include <QtWidgets/QLabel>
#include <QtWidgets/QMenu>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QPlainTextEdit>
#include <QtGui/QAction>
#include <QtGui/QFontDatabase>
#include <QtGui/QIcon>
+#include <QtGui/QTextCursor>
#include <QtCore/QDebug>
#include <QtCore/QTextStream>
m_scriptEdit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
setCentralWidget(m_scriptEdit);
+ m_lineNumberLabel = new QLabel;
+ statusBar()->addPermanentWidget(m_lineNumberLabel, 0);
+ connect(m_scriptEdit, &QPlainTextEdit::cursorPositionChanged,
+ this, &MainWindow::slotCursorChanged);
+
if (!PythonUtils::bindAppObject("__main__"_L1, "mainWindow"_L1,
PythonUtils::MainWindowType, this)) {
statusBar()->showMessage(tr("Error loading the application module"));
qDebug().noquote() << message;
statusBar()->showMessage(message);
}
+
+void MainWindow::slotCursorChanged()
+{
+ auto cursor = m_scriptEdit->textCursor();
+ const int line = cursor.blockNumber() + 1;
+ m_lineNumberLabel->setText("Line: "_L1 + QString::number(line));
+}
#include <QtWidgets/QMainWindow>
+QT_FORWARD_DECLARE_CLASS(QLabel)
QT_FORWARD_DECLARE_CLASS(QPlainTextEdit)
class MainWindow : public QMainWindow
static constexpr auto TEST = QLatin1StringView("test");
private Q_SLOTS:
+ void slotCursorChanged();
void slotRunScript();
void slotPrintDiagnostics();
void runScript(const QString &);
QPlainTextEdit *m_scriptEdit;
+ QLabel *m_lineNumberLabel;
};
#endif // MAINWINDOW_H
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWidgets import QComboBox
from PySide6.QtGui import QIntValidator
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QGroupBox
def _set_readonly_and_compact(box):
- box.setAttribute(Qt.WA_TransparentForMouseEvents)
- box.setFocusPolicy(Qt.NoFocus)
+ box.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
+ box.setFocusPolicy(Qt.FocusPolicy.NoFocus)
box.setStyleSheet("margin-top:0; margin-bottom:0;")
<x>0</x>
<y>0</y>
<width>319</width>
- <height>217</height>
+ <height>257</height>
</rect>
</property>
<property name="windowTitle">
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWidgets import QDialog
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QSettings, Qt, Slot
from PySide6.QtGui import QIntValidator
self.m_ui.useConfigurationBox.toggled.connect(self.m_ui.configurationBox.setEnabled)
self.m_ui.pluginListBox.currentTextChanged.connect(self.plugin_changed)
self.m_ui.interfaceListBox.currentTextChanged.connect(self.interface_changed)
- self.m_ui.ringBufferBox.stateChanged.connect(self._ring_buffer_changed)
+ self.m_ui.ringBufferBox.checkStateChanged.connect(self._ring_buffer_changed)
self.m_ui.rawFilterEdit.hide()
self.m_ui.rawFilterLabel.hide()
@Slot(int)
def _ring_buffer_changed(self, state):
- self.m_ui.ringBufferLimitBox.setEnabled(state == Qt.CheckState.Checked.value)
+ self.m_ui.ringBufferLimitBox.setEnabled(state == Qt.CheckState.Checked)
def settings(self):
return self.m_currentSettings
if k == key:
result = v
break
- if not result and (key == QCanBusDevice.LoopbackKey or key == QCanBusDevice.ReceiveOwnKey):
+ if (not result and (key == QCanBusDevice.ConfigurationKey.LoopbackKey
+ or key == QCanBusDevice.ConfigurationKey.ReceiveOwnKey)):
return "unspecified"
return str(result)
self.m_ui.ringBufferLimitBox.setValue(self.m_currentSettings.model_ring_buffer_size)
self.m_ui.autoscrollBox.setChecked(self.m_currentSettings.use_autoscroll)
- value = self.configuration_value(QCanBusDevice.LoopbackKey)
+ value = self.configuration_value(QCanBusDevice.ConfigurationKey.LoopbackKey)
self.m_ui.loopbackBox.setCurrentText(value)
- value = self.configuration_value(QCanBusDevice.ReceiveOwnKey)
+ value = self.configuration_value(QCanBusDevice.ConfigurationKey.ReceiveOwnKey)
self.m_ui.receiveOwnBox.setCurrentText(value)
- value = self.configuration_value(QCanBusDevice.ErrorFilterKey)
+ value = self.configuration_value(QCanBusDevice.ConfigurationKey.ErrorFilterKey)
self.m_ui.errorFilterEdit.setText(value)
- value = self.configuration_value(QCanBusDevice.BitRateKey)
+ value = self.configuration_value(QCanBusDevice.ConfigurationKey.BitRateKey)
self.m_ui.bitrateBox.setCurrentText(value)
- value = self.configuration_value(QCanBusDevice.CanFdKey)
+ value = self.configuration_value(QCanBusDevice.ConfigurationKey.CanFdKey)
self.m_ui.canFdBox.setCurrentText(value)
- value = self.configuration_value(QCanBusDevice.DataBitRateKey)
+ value = self.configuration_value(QCanBusDevice.ConfigurationKey.DataBitRateKey)
self.m_ui.dataBitrateBox.setCurrentText(value)
def update_settings(self):
<number>10</number>
</property>
<property name="stepType">
- <enum>QAbstractSpinBox::AdaptiveDecimalStepType</enum>
+ <enum>QAbstractSpinBox::StepType::AdaptiveDecimalStepType</enum>
</property>
<property name="value">
<number>1000</number>
<item row="0" column="1">
<widget class="QLineEdit" name="rawFilterEdit">
<property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="errorFilterEdit">
<property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
<property name="placeholderText">
<string>FrameError bits</string>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QTimer, QUrl, Slot
from PySide6.QtGui import QDesktopServices
self.m_ui.actionDisconnect.setEnabled(True)
self.m_ui.actionDeviceInformation.setEnabled(True)
self.m_ui.sendFrameBox.setEnabled(True)
- config_bit_rate = self.m_can_device.configurationParameter(QCanBusDevice.BitRateKey)
- if config_bit_rate > 0:
- is_can_fd = bool(self.m_can_device.configurationParameter(QCanBusDevice.CanFdKey))
+ config_bit_rate = self.m_can_device.configurationParameter(QCanBusDevice.ConfigurationKey.BitRateKey) # noqa: E501
+ if config_bit_rate is not None and config_bit_rate > 0:
+ is_can_fd = bool(self.m_can_device.configurationParameter(QCanBusDevice.ConfigurationKey.CanFdKey)) # noqa: E501
config_data_bit_rate = self.m_can_device.configurationParameter(
- QCanBusDevice.DataBitRateKey)
+ QCanBusDevice.ConfigurationKey.DataBitRateKey)
bit_rate = config_bit_rate / 1000
if is_can_fd and config_data_bit_rate > 0:
data_bit_rate = config_data_bit_rate / 1000
<item>
<widget class="ReceivedFramesView" name="receivedFramesView">
<property name="editTriggers">
- <set>QAbstractItemView::NoEditTriggers</set>
+ <set>QAbstractItemView::EditTrigger::NoEditTriggers</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
<bool>false</bool>
</property>
<property name="selectionBehavior">
- <enum>QAbstractItemView::SelectRows</enum>
+ <enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<x>0</x>
<y>0</y>
<width>634</width>
- <height>22</height>
+ <height>26</height>
</rect>
</property>
<widget class="QMenu" name="menuCalls">
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from enum import IntEnum
count = 6
-clipboard_text_role = Qt.UserRole + 1
+clipboard_text_role = Qt.ItemDataRole.UserRole + 1
-column_alignment = [Qt.AlignRight | Qt.AlignVCenter, Qt.AlignRight | Qt.AlignVCenter,
- Qt.AlignCenter, Qt.AlignRight | Qt.AlignVCenter,
- Qt.AlignRight | Qt.AlignVCenter, Qt.AlignLeft | Qt.AlignVCenter]
+column_alignment = [Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter,
+ Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter,
+ Qt.AlignmentFlag.AlignCenter,
+ Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter,
+ Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter,
+ Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter]
class ReceivedFramesModel(QAbstractTableModel):
return True
def headerData(self, section, orientation, role):
- if role == Qt.DisplayRole and orientation == Qt.Horizontal:
+ if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
if section == ReceivedFramesModelColumns.number:
return "#"
if section == ReceivedFramesModelColumns.timestamp:
if section == ReceivedFramesModelColumns.data:
return "Data"
- if role == Qt.SizeHintRole and orientation == Qt.Horizontal:
+ if role == Qt.ItemDataRole.SizeHintRole and orientation == Qt.Orientation.Horizontal:
if section == ReceivedFramesModelColumns.number:
return QSize(80, 25)
if section == ReceivedFramesModelColumns.timestamp:
return None
row = index.row()
column = index.column()
- if role == Qt.TextAlignmentRole:
+ if role == Qt.ItemDataRole.TextAlignmentRole:
return column_alignment[index.column()]
- if role == Qt.DisplayRole:
+ if role == Qt.ItemDataRole.AlignmentFlag.DisplayRole:
return self.m_framesQueue[row][column]
if role == clipboard_text_role:
f = self.m_framesQueue[row][column]
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QPoint, Qt, Slot
from PySide6.QtGui import QAction, QKeySequence
def __init__(self, parent):
super().__init__(parent)
- self.setContextMenuPolicy(Qt.CustomContextMenu)
+ self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.customContextMenuRequested.connect(self._context_menu)
@Slot(QPoint)
def set_model(self, model):
super().setModel(model)
for i in range(0, model.columnCount()):
- size = model.headerData(i, Qt.Horizontal, Qt.SizeHintRole)
+ size = model.headerData(i, Qt.Orientation.Horizontal, Qt.ItemDataRole.SizeHintRole)
self.setColumnWidth(i, size.width())
def keyPressEvent(self, event):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import re
<rect>
<x>0</x>
<y>0</y>
- <width>485</width>
- <height>206</height>
+ <width>505</width>
+ <height>219</height>
</rect>
</property>
<property name="windowTitle">
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="sizeConstraint">
- <enum>QLayout::SetMinimumSize</enum>
+ <enum>QLayout::SizeConstraint::SetMinimumSize</enum>
</property>
<item>
<widget class="QGroupBox" name="frameTypeBox">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="sizeConstraint">
- <enum>QLayout::SetMinimumSize</enum>
+ <enum>QLayout::SizeConstraint::SetMinimumSize</enum>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
################################################################################
## Form generated from reading UI file 'canbusdeviceinfobox.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
def setupUi(self, CanBusDeviceInfoBox):
if not CanBusDeviceInfoBox.objectName():
CanBusDeviceInfoBox.setObjectName(u"CanBusDeviceInfoBox")
- CanBusDeviceInfoBox.resize(319, 217)
+ CanBusDeviceInfoBox.resize(319, 257)
self.verticalLayout = QVBoxLayout(CanBusDeviceInfoBox)
self.verticalLayout.setObjectName(u"verticalLayout")
self.pluginLabel = QLabel(CanBusDeviceInfoBox)
################################################################################
## Form generated from reading UI file 'canbusdeviceinfodialog.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
################################################################################
## Form generated from reading UI file 'connectdialog.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.ringBufferLimitBox.setMinimum(10)
self.ringBufferLimitBox.setMaximum(10000000)
self.ringBufferLimitBox.setSingleStep(10)
- self.ringBufferLimitBox.setStepType(QAbstractSpinBox.AdaptiveDecimalStepType)
+ self.ringBufferLimitBox.setStepType(QAbstractSpinBox.StepType.AdaptiveDecimalStepType)
self.ringBufferLimitBox.setValue(1000)
self.horizontalLayout_2.addWidget(self.ringBufferLimitBox)
self.rawFilterEdit = QLineEdit(self.configurationBox)
self.rawFilterEdit.setObjectName(u"rawFilterEdit")
- self.rawFilterEdit.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
+ self.rawFilterEdit.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter)
self.gridLayout_4.addWidget(self.rawFilterEdit, 0, 1, 1, 1)
self.errorFilterEdit = QLineEdit(self.configurationBox)
self.errorFilterEdit.setObjectName(u"errorFilterEdit")
- self.errorFilterEdit.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
+ self.errorFilterEdit.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter)
self.gridLayout_4.addWidget(self.errorFilterEdit, 1, 1, 1, 1)
################################################################################
## Form generated from reading UI file 'mainwindow.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.actionConnect = QAction(MainWindow)
self.actionConnect.setObjectName(u"actionConnect")
icon = QIcon()
- icon.addFile(u":/images/connect.png", QSize(), QIcon.Normal, QIcon.Off)
+ icon.addFile(u":/images/connect.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionConnect.setIcon(icon)
self.actionDisconnect = QAction(MainWindow)
self.actionDisconnect.setObjectName(u"actionDisconnect")
icon1 = QIcon()
- icon1.addFile(u":/images/disconnect.png", QSize(), QIcon.Normal, QIcon.Off)
+ icon1.addFile(u":/images/disconnect.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionDisconnect.setIcon(icon1)
self.actionQuit = QAction(MainWindow)
self.actionQuit.setObjectName(u"actionQuit")
icon2 = QIcon()
- icon2.addFile(u":/images/application-exit.png", QSize(), QIcon.Normal, QIcon.Off)
+ icon2.addFile(u":/images/application-exit.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionQuit.setIcon(icon2)
self.actionAboutQt = QAction(MainWindow)
self.actionAboutQt.setObjectName(u"actionAboutQt")
self.actionClearLog = QAction(MainWindow)
self.actionClearLog.setObjectName(u"actionClearLog")
icon3 = QIcon()
- icon3.addFile(u":/images/clear.png", QSize(), QIcon.Normal, QIcon.Off)
+ icon3.addFile(u":/images/clear.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionClearLog.setIcon(icon3)
self.actionPluginDocumentation = QAction(MainWindow)
self.actionPluginDocumentation.setObjectName(u"actionPluginDocumentation")
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.receivedFramesView = ReceivedFramesView(self.receivedMessagesBox)
self.receivedFramesView.setObjectName(u"receivedFramesView")
- self.receivedFramesView.setEditTriggers(QAbstractItemView.NoEditTriggers)
- self.receivedFramesView.setProperty("showDropIndicator", False)
+ self.receivedFramesView.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
+ self.receivedFramesView.setProperty(u"showDropIndicator", False)
self.receivedFramesView.setDragDropOverwriteMode(False)
- self.receivedFramesView.setSelectionBehavior(QAbstractItemView.SelectRows)
+ self.receivedFramesView.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.verticalLayout_2.addWidget(self.receivedFramesView)
MainWindow.setCentralWidget(self.centralWidget)
self.menuBar = QMenuBar(MainWindow)
self.menuBar.setObjectName(u"menuBar")
- self.menuBar.setGeometry(QRect(0, 0, 634, 22))
+ self.menuBar.setGeometry(QRect(0, 0, 634, 26))
self.menuCalls = QMenu(self.menuBar)
self.menuCalls.setObjectName(u"menuCalls")
self.menuHelp = QMenu(self.menuBar)
################################################################################
## Form generated from reading UI file 'sendframebox.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
def setupUi(self, SendFrameBox):
if not SendFrameBox.objectName():
SendFrameBox.setObjectName(u"SendFrameBox")
- SendFrameBox.resize(485, 206)
+ SendFrameBox.resize(505, 219)
self.verticalLayout_4 = QVBoxLayout(SendFrameBox)
self.verticalLayout_4.setObjectName(u"verticalLayout_4")
- self.verticalLayout_4.setSizeConstraint(QLayout.SetMinimumSize)
+ self.verticalLayout_4.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize)
self.frameTypeBox = QGroupBox(SendFrameBox)
self.frameTypeBox.setObjectName(u"frameTypeBox")
self.frameTypeBox.setCheckable(False)
self.horizontalLayout_3 = QHBoxLayout()
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
- self.horizontalLayout_3.setSizeConstraint(QLayout.SetMinimumSize)
+ self.horizontalLayout_3.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize)
self.verticalLayout = QVBoxLayout()
self.verticalLayout.setObjectName(u"verticalLayout")
self.frameIdLabel = QLabel(SendFrameBox)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the examples/serialbus/modbus/client example from Qt v6.x"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
-import functools
from enum import IntEnum
from PySide6.QtCore import QUrl, Slot
from PySide6.QtGui import QStandardItemModel, QStandardItem
from PySide6.QtWidgets import QMainWindow
-from PySide6.QtSerialBus import (QModbusDataUnit,
- QModbusDevice, QModbusReply,
+from PySide6.QtSerialBus import (QModbusDataUnit, QModbusDevice,
QModbusRtuSerialClient, QModbusTcpClient)
from ui_mainwindow import Ui_MainWindow
vp = self.ui.writeValueTable.viewport()
self._write_model.update_viewport.connect(vp.update)
- self.ui.writeTable.addItem("Coils", QModbusDataUnit.Coils)
- self.ui.writeTable.addItem("Discrete Inputs", QModbusDataUnit.DiscreteInputs)
- self.ui.writeTable.addItem("Input Registers", QModbusDataUnit.InputRegisters)
- self.ui.writeTable.addItem("Holding Registers", QModbusDataUnit.HoldingRegisters)
+ self.ui.writeTable.addItem("Coils", QModbusDataUnit.RegisterType.Coils)
+ self.ui.writeTable.addItem("Discrete Inputs", QModbusDataUnit.RegisterType.DiscreteInputs)
+ self.ui.writeTable.addItem("Input Registers", QModbusDataUnit.RegisterType.InputRegisters)
+ self.ui.writeTable.addItem("Holding Registers",
+ QModbusDataUnit.RegisterType.HoldingRegisters)
self.ui.connectType.setCurrentIndex(0)
self.onConnectTypeChanged(0)
elif index == ModbusConnection.TCP:
self._modbus_device = QModbusTcpClient(self)
if not self.ui.portEdit.text():
- self.ui.portEdit.setText("127.0.0.1:502")
+ self.ui.portEdit.setText("127.0.0.1:50200")
self._modbus_device.errorOccurred.connect(self._show_device_errorstring)
self.statusBar().clearMessage()
md = self._modbus_device
- if md.state() != QModbusDevice.ConnectedState:
+ if md.state() != QModbusDevice.State.ConnectedState:
settings = self._settings_dialog.settings()
if self.ui.connectType.currentIndex() == ModbusConnection.SERIAL:
- md.setConnectionParameter(QModbusDevice.SerialPortNameParameter,
+ md.setConnectionParameter(QModbusDevice.ConnectionParameter.SerialPortNameParameter,
self.ui.portEdit.text())
- md.setConnectionParameter(QModbusDevice.SerialParityParameter,
+ md.setConnectionParameter(QModbusDevice.ConnectionParameter.SerialParityParameter,
settings.parity)
- md.setConnectionParameter(QModbusDevice.SerialBaudRateParameter,
+ md.setConnectionParameter(QModbusDevice.ConnectionParameter.SerialBaudRateParameter,
settings.baud)
- md.setConnectionParameter(QModbusDevice.SerialDataBitsParameter,
+ md.setConnectionParameter(QModbusDevice.ConnectionParameter.SerialDataBitsParameter,
settings.data_bits)
- md.setConnectionParameter(QModbusDevice.SerialStopBitsParameter,
+ md.setConnectionParameter(QModbusDevice.ConnectionParameter.SerialStopBitsParameter,
settings.stop_bits)
else:
url = QUrl.fromUserInput(self.ui.portEdit.text())
- md.setConnectionParameter(QModbusDevice.NetworkPortParameter,
+ md.setConnectionParameter(QModbusDevice.ConnectionParameter.NetworkPortParameter,
url.port())
- md.setConnectionParameter(QModbusDevice.NetworkAddressParameter,
+ md.setConnectionParameter(QModbusDevice.ConnectionParameter.NetworkAddressParameter,
url.host())
md.setTimeout(settings.response_time)
@Slot(int)
def onModbusStateChanged(self, state):
- connected = (state != QModbusDevice.UnconnectedState)
+ connected = (state != QModbusDevice.State.UnconnectedState)
self.ui.actionConnect.setEnabled(not connected)
self.ui.actionDisconnect.setEnabled(connected)
- if state == QModbusDevice.UnconnectedState:
+ if state == QModbusDevice.State.UnconnectedState:
self.ui.connectButton.setText("Connect")
- elif state == QModbusDevice.ConnectedState:
+ elif state == QModbusDevice.State.ConnectedState:
self.ui.connectButton.setText("Disconnect")
@Slot()
self.ui.serverEdit.value())
if reply:
if not reply.isFinished():
- reply.finished.connect(functools.partial(self.onReadReady, reply))
+ reply.finished.connect(self.onReadReady)
else:
del reply # broadcast replies return immediately
else:
self.statusBar().showMessage(message, 5000)
@Slot()
- def onReadReady(self, reply):
+ def onReadReady(self):
+ reply = self.sender()
if not reply:
return
table = write_unit.registerType()
for i in range(0, total):
addr = i + write_unit.startAddress()
- if table == QModbusDataUnit.Coils:
+ if table == QModbusDataUnit.RegisterType.Coils:
write_unit.setValue(i, self._write_model.m_coils[addr])
else:
write_unit.setValue(i, self._write_model.m_holdingRegisters[addr])
# broadcast replies return immediately
reply.deleteLater()
else:
- reply.finished.connect(functools.partial(self._write_finished, reply))
+ reply.finished.connect(self._write_finished)
else:
message = "Write error: " + self._modbus_device.errorString()
self.statusBar().showMessage(message, 5000)
- @Slot(QModbusReply)
- def _write_finished(self, reply):
+ @Slot()
+ def _write_finished(self):
+ reply = self.sender()
+ if not reply:
+ return
error = reply.error()
if error == QModbusDevice.ProtocolError:
e = reply.errorString()
self.ui.serverEdit.value())
if reply:
if not reply.isFinished():
- reply.finished.connect(functools.partial(self.onReadReady, reply))
+ reply.finished.connect(self.onReadReady)
else:
del reply # broadcast replies return immediately
else:
<rect>
<x>0</x>
<y>0</y>
- <width>516</width>
+ <width>601</width>
<height>378</height>
</rect>
</property>
<item row="0" column="4">
<spacer name="horizontalSpacer">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<rect>
<x>0</x>
<y>0</y>
- <width>516</width>
- <height>21</height>
+ <width>601</width>
+ <height>26</height>
</rect>
</property>
<widget class="QMenu" name="menuDevice">
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Slot
from PySide6.QtWidgets import QDialog
class Settings:
def __init__(self):
- self.parity = QSerialPort.EvenParity
- self.baud = QSerialPort.Baud19200
- self.data_bits = QSerialPort.Data8
- self.stop_bits = QSerialPort.OneStop
+ self.parity = QSerialPort.Parity.EvenParity
+ self.baud = QSerialPort.BaudRate.Baud19200
+ self.data_bits = QSerialPort.DataBits.Data8
+ self.stop_bits = QSerialPort.StopBits.OneStop
self.response_time = 1000
self.number_of_retries = 3
<rect>
<x>0</x>
<y>0</y>
- <width>239</width>
- <height>256</height>
+ <width>259</width>
+ <height>321</height>
</rect>
</property>
<property name="windowTitle">
<item row="3" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
- <enum>Qt::Vertical</enum>
+ <enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
################################################################################
## Form generated from reading UI file 'mainwindow.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
def setupUi(self, MainWindow):
if not MainWindow.objectName():
MainWindow.setObjectName(u"MainWindow")
- MainWindow.resize(516, 378)
+ MainWindow.resize(601, 378)
MainWindow.setMaximumSize(QSize(16777215, 1000))
self.actionConnect = QAction(MainWindow)
self.actionConnect.setObjectName(u"actionConnect")
icon = QIcon()
- icon.addFile(u":/images/connect.png", QSize(), QIcon.Normal, QIcon.Off)
+ icon.addFile(u":/images/connect.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionConnect.setIcon(icon)
self.actionDisconnect = QAction(MainWindow)
self.actionDisconnect.setObjectName(u"actionDisconnect")
icon1 = QIcon()
- icon1.addFile(u":/images/disconnect.png", QSize(), QIcon.Normal, QIcon.Off)
+ icon1.addFile(u":/images/disconnect.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionDisconnect.setIcon(icon1)
self.actionExit = QAction(MainWindow)
self.actionExit.setObjectName(u"actionExit")
icon2 = QIcon()
- icon2.addFile(u":/images/application-exit.png", QSize(), QIcon.Normal, QIcon.Off)
+ icon2.addFile(u":/images/application-exit.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionExit.setIcon(icon2)
self.actionOptions = QAction(MainWindow)
self.actionOptions.setObjectName(u"actionOptions")
icon3 = QIcon()
- icon3.addFile(u":/images/settings.png", QSize(), QIcon.Normal, QIcon.Off)
+ icon3.addFile(u":/images/settings.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionOptions.setIcon(icon3)
self.centralWidget = QWidget(MainWindow)
self.centralWidget.setObjectName(u"centralWidget")
self.writeValueTable = QTreeView(self.writeGroupBox)
self.writeValueTable.setObjectName(u"writeValueTable")
- self.writeValueTable.setProperty("showDropIndicator", True)
+ self.writeValueTable.setProperty(u"showDropIndicator", True)
self.writeValueTable.setAlternatingRowColors(True)
self.writeValueTable.setRootIsDecorated(False)
self.writeValueTable.setUniformRowHeights(True)
MainWindow.setStatusBar(self.statusBar)
self.menuBar = QMenuBar(MainWindow)
self.menuBar.setObjectName(u"menuBar")
- self.menuBar.setGeometry(QRect(0, 0, 516, 21))
+ self.menuBar.setGeometry(QRect(0, 0, 601, 26))
self.menuDevice = QMenu(self.menuBar)
self.menuDevice.setObjectName(u"menuDevice")
self.menuToo_ls = QMenu(self.menuBar)
################################################################################
## Form generated from reading UI file 'settingsdialog.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
def setupUi(self, SettingsDialog):
if not SettingsDialog.objectName():
SettingsDialog.setObjectName(u"SettingsDialog")
- SettingsDialog.resize(239, 256)
+ SettingsDialog.resize(259, 321)
self.gridLayout = QGridLayout(SettingsDialog)
self.gridLayout.setObjectName(u"gridLayout")
self.verticalSpacer = QSpacerItem(20, 43, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from enum import IntEnum
assert self.m_coils.size() == Column.ROW_COUNT
assert len(self.m_holdingRegisters) == Column.ROW_COUNT
- if column == Column.NUM_COLUMN and role == Qt.DisplayRole:
+ if column == Column.NUM_COLUMN and role == Qt.ItemDataRole.DisplayRole:
return f"{row}"
- if column == Column.COILS_COLUMN and role == Qt.CheckStateRole: # coils
- return Qt.Checked if self.m_coils[row] else Qt.Unchecked
+ if column == Column.COILS_COLUMN and role == Qt.ItemDataRole.CheckStateRole: # coils
+ return Qt.CheckState.Checked if self.m_coils[row] else Qt.CheckState.Unchecked
# holding registers
- if column == Column.HOLDING_COLUMN and role == Qt.DisplayRole:
+ if column == Column.HOLDING_COLUMN and role == Qt.ItemDataRole.DisplayRole:
reg = self.m_holdingRegisters[row]
return f"0x{reg:x}"
return None
def headerData(self, section, orientation, role):
- if role != Qt.DisplayRole:
+ if role != Qt.ItemDataRole.DisplayRole:
return None
- if orientation == Qt.Horizontal:
+ if orientation == Qt.Orientation.Horizontal:
if section == Column.NUM_COLUMN:
return "#"
if section == Column.COILS_COLUMN:
assert self.m_coils.size() == Column.ROW_COUNT
assert len(self.m_holdingRegisters) == Column.ROW_COUNT
- if column == Column.COILS_COLUMN and role == Qt.CheckStateRole: # coils
+ if column == Column.COILS_COLUMN and role == Qt.ItemDataRole.CheckStateRole: # coils
s = Qt.CheckState(int(value))
- if s == Qt.Checked:
+ if s == Qt.CheckState.Checked:
self.m_coils.setBit(row)
else:
self.m_coils.clearBit(row)
self.dataChanged.emit(index, index)
return True
- if column == Column.HOLDING_COLUMN and role == Qt.EditRole: # holding registers
+ if column == Column.HOLDING_COLUMN and role == Qt.ItemDataRole.EditRole:
+ # holding registers
base = 16 if value.startswith("0x") else 10
self.m_holdingRegisters[row] = int(value, base=base)
self.dataChanged.emit(index, index)
return flags
if row < self.m_address or row >= (self.m_address + self.m_number):
- flags &= ~Qt.ItemIsEnabled
+ flags &= ~Qt.ItemFlag.ItemIsEnabled
if column == Column.COILS_COLUMN: # coils
- return flags | Qt.ItemIsUserCheckable
+ return flags | Qt.ItemFlag.ItemIsUserCheckable
if column == Column.HOLDING_COLUMN: # holding registers
- return flags | Qt.ItemIsEditable
+ return flags | Qt.ItemFlag.ItemIsEditable
return flags
@Slot(int)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Qt, Signal, Slot
from PySide6.QtGui import QPalette
from PySide6.QtWidgets import QPlainTextEdit
-UNHANDLED_KEYS = [Qt.Key_Backspace, Qt.Key_Left, Qt.Key_Right, Qt.Key_Up,
- Qt.Key_Down]
+UNHANDLED_KEYS = [Qt.Key.Key_Backspace, Qt.Key.Key_Left, Qt.Key.Key_Right, Qt.Key.Key_Up,
+ Qt.Key.Key_Down]
class Console(QPlainTextEdit):
self.m_localEchoEnabled = False
self.document().setMaximumBlockCount(100)
p = self.palette()
- p.setColor(QPalette.Base, Qt.black)
- p.setColor(QPalette.Text, Qt.green)
+ p.setColor(QPalette.ColorRole.Base, Qt.GlobalColor.black)
+ p.setColor(QPalette.ColorRole.Text, Qt.GlobalColor.green)
self.setPalette(p)
@Slot(bytearray)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QIODeviceBase, Slot
from PySide6.QtWidgets import QLabel, QMainWindow, QMessageBox
self.m_serial.setParity(s.parity)
self.m_serial.setStopBits(s.stop_bits)
self.m_serial.setFlowControl(s.flow_control)
- if self.m_serial.open(QIODeviceBase.ReadWrite):
+ if self.m_serial.open(QIODeviceBase.OpenModeFlag.ReadWrite):
self.m_console.setEnabled(True)
self.m_console.set_local_echo_enabled(s.local_echo_enabled)
self.m_ui.actionConnect.setEnabled(False)
@Slot(QSerialPort.SerialPortError)
def handle_error(self, error):
- if error == QSerialPort.ResourceError:
+ if error == QSerialPort.SerialPortError.ResourceError:
QMessageBox.critical(self, "Critical Error",
self.m_serial.errorString())
self.close_serial_port()
<x>0</x>
<y>0</y>
<width>400</width>
- <height>19</height>
+ <height>26</height>
</rect>
</property>
<widget class="QMenu" name="menuCalls">
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
self.name = ""
self.baud_rate = 0
self.string_baud_rate = ""
- self.data_bits = QSerialPort.Data8
+ self.data_bits = QSerialPort.DataBits.Data8
self.string_data_bits = ""
- self.parity = QSerialPort.NoParity
+ self.parity = QSerialPort.Parity.NoParity
self.string_parity = ""
- self.stop_bits = QSerialPort.OneStop
+ self.stop_bits = QSerialPort.StopBits.OneStop
self.string_stop_bits = ""
- self.flow_control = QSerialPort.SoftwareControl
+ self.flow_control = QSerialPort.FlowControl.SoftwareControl
self.string_flow_control = ""
self.local_echo_enabled = False
self.m_currentSettings = Settings()
self.m_intValidator = QIntValidator(0, 4000000, self)
- self.m_ui.baudRateBox.setInsertPolicy(QComboBox.NoInsert)
+ self.m_ui.baudRateBox.setInsertPolicy(QComboBox.InsertPolicy.NoInsert)
self.m_ui.applyButton.clicked.connect(self.apply)
self.m_ui.serialPortInfoListBox.currentIndexChanged.connect(self.show_port_info)
self.m_ui.serialPortInfoListBox.clearEditText()
def fill_ports_parameters(self):
- self.m_ui.baudRateBox.addItem("9600", QSerialPort.Baud9600)
- self.m_ui.baudRateBox.addItem("19200", QSerialPort.Baud19200)
- self.m_ui.baudRateBox.addItem("38400", QSerialPort.Baud38400)
- self.m_ui.baudRateBox.addItem("115200", QSerialPort.Baud115200)
+ self.m_ui.baudRateBox.addItem("9600", QSerialPort.BaudRate.Baud9600)
+ self.m_ui.baudRateBox.addItem("19200", QSerialPort.BaudRate.Baud19200)
+ self.m_ui.baudRateBox.addItem("38400", QSerialPort.BaudRate.Baud38400)
+ self.m_ui.baudRateBox.addItem("115200", QSerialPort.BaudRate.Baud115200)
self.m_ui.baudRateBox.addItem("Custom")
- self.m_ui.dataBitsBox.addItem("5", QSerialPort.Data5)
- self.m_ui.dataBitsBox.addItem("6", QSerialPort.Data6)
- self.m_ui.dataBitsBox.addItem("7", QSerialPort.Data7)
- self.m_ui.dataBitsBox.addItem("8", QSerialPort.Data8)
+ self.m_ui.dataBitsBox.addItem("5", QSerialPort.DataBits.Data5)
+ self.m_ui.dataBitsBox.addItem("6", QSerialPort.DataBits.Data6)
+ self.m_ui.dataBitsBox.addItem("7", QSerialPort.DataBits.Data7)
+ self.m_ui.dataBitsBox.addItem("8", QSerialPort.DataBits.Data8)
self.m_ui.dataBitsBox.setCurrentIndex(3)
- self.m_ui.parityBox.addItem("None", QSerialPort.NoParity)
- self.m_ui.parityBox.addItem("Even", QSerialPort.EvenParity)
- self.m_ui.parityBox.addItem("Odd", QSerialPort.OddParity)
- self.m_ui.parityBox.addItem("Mark", QSerialPort.MarkParity)
- self.m_ui.parityBox.addItem("Space", QSerialPort.SpaceParity)
+ self.m_ui.parityBox.addItem("None", QSerialPort.Parity.NoParity)
+ self.m_ui.parityBox.addItem("Even", QSerialPort.Parity.EvenParity)
+ self.m_ui.parityBox.addItem("Odd", QSerialPort.Parity.OddParity)
+ self.m_ui.parityBox.addItem("Mark", QSerialPort.Parity.MarkParity)
+ self.m_ui.parityBox.addItem("Space", QSerialPort.Parity.SpaceParity)
- self.m_ui.stopBitsBox.addItem("1", QSerialPort.OneStop)
+ self.m_ui.stopBitsBox.addItem("1", QSerialPort.StopBits.OneStop)
if sys.platform == "win32":
- self.m_ui.stopBitsBox.addItem("1.5", QSerialPort.OneAndHalfStop)
+ self.m_ui.stopBitsBox.addItem("1.5", QSerialPort.StopBits.OneAndHalfStop)
- self.m_ui.stopBitsBox.addItem("2", QSerialPort.TwoStop)
+ self.m_ui.stopBitsBox.addItem("2", QSerialPort.StopBits.TwoStop)
- self.m_ui.flowControlBox.addItem("None", QSerialPort.NoFlowControl)
- self.m_ui.flowControlBox.addItem("RTS/CTS", QSerialPort.HardwareControl)
- self.m_ui.flowControlBox.addItem("XON/XOFF", QSerialPort.SoftwareControl)
+ self.m_ui.flowControlBox.addItem("None", QSerialPort.FlowControl.NoFlowControl)
+ self.m_ui.flowControlBox.addItem("RTS/CTS", QSerialPort.FlowControl.HardwareControl)
+ self.m_ui.flowControlBox.addItem("XON/XOFF", QSerialPort.FlowControl.SoftwareControl)
def fill_ports_info(self):
self.m_ui.serialPortInfoListBox.clear()
<rect>
<x>0</x>
<y>0</y>
- <width>281</width>
- <height>262</height>
+ <width>392</width>
+ <height>386</height>
</rect>
</property>
<property name="windowTitle">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
################################################################################
## Form generated from reading UI file 'mainwindow.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.actionConnect = QAction(MainWindow)
self.actionConnect.setObjectName(u"actionConnect")
icon = QIcon()
- icon.addFile(u":/images/connect.png", QSize(), QIcon.Normal, QIcon.Off)
+ icon.addFile(u":/images/connect.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionConnect.setIcon(icon)
self.actionDisconnect = QAction(MainWindow)
self.actionDisconnect.setObjectName(u"actionDisconnect")
icon1 = QIcon()
- icon1.addFile(u":/images/disconnect.png", QSize(), QIcon.Normal, QIcon.Off)
+ icon1.addFile(u":/images/disconnect.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionDisconnect.setIcon(icon1)
self.actionConfigure = QAction(MainWindow)
self.actionConfigure.setObjectName(u"actionConfigure")
icon2 = QIcon()
- icon2.addFile(u":/images/settings.png", QSize(), QIcon.Normal, QIcon.Off)
+ icon2.addFile(u":/images/settings.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionConfigure.setIcon(icon2)
self.actionClear = QAction(MainWindow)
self.actionClear.setObjectName(u"actionClear")
icon3 = QIcon()
- icon3.addFile(u":/images/clear.png", QSize(), QIcon.Normal, QIcon.Off)
+ icon3.addFile(u":/images/clear.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionClear.setIcon(icon3)
self.actionQuit = QAction(MainWindow)
self.actionQuit.setObjectName(u"actionQuit")
icon4 = QIcon()
- icon4.addFile(u":/images/application-exit.png", QSize(), QIcon.Normal, QIcon.Off)
+ icon4.addFile(u":/images/application-exit.png", QSize(), QIcon.Mode.Normal, QIcon.State.Off)
self.actionQuit.setIcon(icon4)
self.centralWidget = QWidget(MainWindow)
self.centralWidget.setObjectName(u"centralWidget")
MainWindow.setCentralWidget(self.centralWidget)
self.menuBar = QMenuBar(MainWindow)
self.menuBar.setObjectName(u"menuBar")
- self.menuBar.setGeometry(QRect(0, 0, 400, 19))
+ self.menuBar.setGeometry(QRect(0, 0, 400, 26))
self.menuCalls = QMenu(self.menuBar)
self.menuCalls.setObjectName(u"menuCalls")
self.menuTools = QMenu(self.menuBar)
################################################################################
## Form generated from reading UI file 'settingsdialog.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
def setupUi(self, SettingsDialog):
if not SettingsDialog.objectName():
SettingsDialog.setObjectName(u"SettingsDialog")
- SettingsDialog.resize(281, 262)
+ SettingsDialog.resize(392, 386)
self.gridLayout_3 = QGridLayout(SettingsDialog)
self.gridLayout_3.setObjectName(u"gridLayout_3")
self.parametersBox = QGroupBox(SettingsDialog)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import math
import sys
file_layout.addWidget(self._file_dialog_button)
form.addRow(file_layout)
- self._azimuth = QSlider(Qt.Horizontal)
+ self._azimuth = QSlider(Qt.Orientation.Horizontal)
self._azimuth.setRange(-180, 180)
form.addRow("Azimuth (-180 - 180 degree):", self._azimuth)
- self._elevation = QSlider(Qt.Horizontal)
+ self._elevation = QSlider(Qt.Orientation.Horizontal)
self._elevation.setRange(-90, 90)
form.addRow("Elevation (-90 - 90 degree)", self._elevation)
- self._distance = QSlider(Qt.Horizontal)
+ self._distance = QSlider(Qt.Orientation.Horizontal)
self._distance.setRange(0, 1000)
self._distance.setValue(100)
form.addRow("Distance (0 - 10 meter):", self._distance)
- self._occlusion = QSlider(Qt.Horizontal)
+ self._occlusion = QSlider(Qt.Orientation.Horizontal)
self._occlusion.setRange(0, 400)
form.addRow("Occlusion (0 - 4):", self._occlusion)
- self._room_dimension = QSlider(Qt.Horizontal)
+ self._room_dimension = QSlider(Qt.Orientation.Horizontal)
self._room_dimension.setRange(0, 10000)
self._room_dimension.setValue(1000)
form.addRow("Room dimension (0 - 100 meter):", self._room_dimension)
- self._reverb_gain = QSlider(Qt.Horizontal)
+ self._reverb_gain = QSlider(Qt.Orientation.Horizontal)
self._reverb_gain.setRange(0, 500)
self._reverb_gain.setValue(0)
form.addRow("Reverb gain (0-5):", self._reverb_gain)
- self._reflection_gain = QSlider(Qt.Horizontal)
+ self._reflection_gain = QSlider(Qt.Orientation.Horizontal)
self._reflection_gain.setRange(0, 500)
self._reflection_gain.setValue(0)
form.addRow("Reflection gain (0-5):", self._reflection_gain)
self._mode = QComboBox()
- self._mode.addItem("Surround", QAudioEngine.Surround)
- self._mode.addItem("Stereo", QAudioEngine.Stereo)
- self._mode.addItem("Headphone", QAudioEngine.Headphone)
+ self._mode.addItem("Surround", QAudioEngine.OutputMode.Surround)
+ self._mode.addItem("Stereo", QAudioEngine.OutputMode.Stereo)
+ self._mode.addItem("Headphone", QAudioEngine.OutputMode.Headphone)
form.addRow("Output mode:", self._mode)
self._engine = QAudioEngine()
self._room = QAudioRoom(self._engine)
- self._room.setWallMaterial(QAudioRoom.BackWall, QAudioRoom.BrickBare)
- self._room.setWallMaterial(QAudioRoom.FrontWall, QAudioRoom.BrickBare)
- self._room.setWallMaterial(QAudioRoom.LeftWall, QAudioRoom.BrickBare)
- self._room.setWallMaterial(QAudioRoom.RightWall, QAudioRoom.BrickBare)
- self._room.setWallMaterial(QAudioRoom.Floor, QAudioRoom.Marble)
- self._room.setWallMaterial(QAudioRoom.Ceiling, QAudioRoom.WoodCeiling)
+ self._room.setWallMaterial(QAudioRoom.Wall.BackWall, QAudioRoom.Material.BrickBare)
+ self._room.setWallMaterial(QAudioRoom.Wall.FrontWall, QAudioRoom.Material.BrickBare)
+ self._room.setWallMaterial(QAudioRoom.Wall.LeftWall, QAudioRoom.Material.BrickBare)
+ self._room.setWallMaterial(QAudioRoom.Wall.RightWall, QAudioRoom.Material.BrickBare)
+ self._room.setWallMaterial(QAudioRoom.Wall.Floor, QAudioRoom.Material.Marble)
+ self._room.setWallMaterial(QAudioRoom.Wall.Ceiling, QAudioRoom.Material.WoodCeiling)
self.update_room()
self._listener = QAudioListener(self._engine)
def file_changed(self, file):
self._sound.setSource(QUrl.fromLocalFile(file))
self._sound.setSize(5)
- self._sound.setLoops(QSpatialSound.Infinite)
+ self._sound.setLoops(QSpatialSound.Loops.Infinite)
@Slot()
def open_file_dialog(self):
if not self._file_dialog:
- directory = QStandardPaths.writableLocation(QStandardPaths.MusicLocation)
+ directory = QStandardPaths.writableLocation(QStandardPaths.StandardLocation.MusicLocation) # noqa: E501
self._file_dialog = QFileDialog(self, "Open Audio File", directory)
- self._file_dialog.setAcceptMode(QFileDialog.AcceptOpen)
+ self._file_dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptOpen)
mime_types = ["audio/mpeg", "audio/aac", "audio/x-ms-wma",
"audio/x-flac+ogg", "audio/x-wav"]
self._file_dialog.setMimeTypeFilters(mime_types)
self._file_dialog.selectMimeTypeFilter(mime_types[0])
- if self._file_dialog.exec() == QDialog.Accepted:
+ if self._file_dialog.exec() == QDialog.DialogCode.Accepted:
self._file_edit.setText(self._file_dialog.selectedFiles()[0])
@Slot()
.. image:: hello_speak.webp
:width: 400
:alt: Hello Speak Screenshot
-
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the hello_speak example from Qt v6.x"""
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QLocale, QSignalBlocker, Slot
from PySide6.QtWidgets import QMainWindow
<string>Engine</string>
</property>
<property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<string>Pitch:</string>
</property>
<property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<string>&Language:</string>
</property>
<property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>language</cstring>
<number>1</number>
</property>
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
<string>Voice name:</string>
</property>
<property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<number>70</number>
</property>
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
<string>Rate:</string>
</property>
<property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<string>Volume:</string>
</property>
<property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<number>10</number>
</property>
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
- <enum>Qt::Vertical</enum>
+ <enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
################################################################################
## Form generated from reading UI file 'mainwindow.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.label_5.sizePolicy().hasHeightForWidth())
self.label_5.setSizePolicy(sizePolicy1)
- self.label_5.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
+ self.label_5.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter)
self.gridLayout.addWidget(self.label_5, 4, 0, 1, 1)
self.label_3.setObjectName(u"label_3")
sizePolicy1.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth())
self.label_3.setSizePolicy(sizePolicy1)
- self.label_3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
+ self.label_3.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter)
self.gridLayout.addWidget(self.label_3, 3, 0, 1, 1)
self.label_4.setObjectName(u"label_4")
sizePolicy1.setHeightForWidth(self.label_4.sizePolicy().hasHeightForWidth())
self.label_4.setSizePolicy(sizePolicy1)
- self.label_4.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
+ self.label_4.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter)
self.gridLayout.addWidget(self.label_4, 5, 0, 1, 1)
self.pitch.setMinimum(-10)
self.pitch.setMaximum(10)
self.pitch.setSingleStep(1)
- self.pitch.setOrientation(Qt.Horizontal)
+ self.pitch.setOrientation(Qt.Orientation.Horizontal)
self.gridLayout.addWidget(self.pitch, 3, 2, 1, 1)
self.label_6 = QLabel(self.centralwidget)
self.label_6.setObjectName(u"label_6")
- self.label_6.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
+ self.label_6.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter)
self.gridLayout.addWidget(self.label_6, 6, 0, 1, 1)
self.volume.setSingleStep(5)
self.volume.setPageStep(20)
self.volume.setValue(70)
- self.volume.setOrientation(Qt.Horizontal)
+ self.volume.setOrientation(Qt.Orientation.Horizontal)
self.gridLayout.addWidget(self.volume, 1, 2, 1, 1)
self.label.setObjectName(u"label")
sizePolicy1.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
self.label.setSizePolicy(sizePolicy1)
- self.label.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
+ self.label.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter)
self.gridLayout.addWidget(self.label, 2, 0, 1, 1)
self.label_2.setObjectName(u"label_2")
sizePolicy1.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
self.label_2.setSizePolicy(sizePolicy1)
- self.label_2.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
+ self.label_2.setAlignment(Qt.AlignmentFlag.AlignRight|Qt.AlignmentFlag.AlignTrailing|Qt.AlignmentFlag.AlignVCenter)
self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
self.rate.setObjectName(u"rate")
self.rate.setMinimum(-10)
self.rate.setMaximum(10)
- self.rate.setOrientation(Qt.Horizontal)
+ self.rate.setOrientation(Qt.Orientation.Horizontal)
self.gridLayout.addWidget(self.rate, 2, 2, 1, 1)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import copy
from PySide6.QtSql import QSqlRelationalDelegate
if option.state & QStyle.State_Selected:
painter.fillRect(option.rect,
option.palette.color(color_group, QPalette.Highlight))
- rating = model.data(index, Qt.DisplayRole)
+ rating = model.data(index, Qt.ItemDataRole.DisplayRole)
width = self.star.width()
height = self.star.height()
x = option.rect.x()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWidgets import (QAbstractItemView, QDataWidgetMapper,
QHeaderView, QMainWindow, QMessageBox)
model.setRelation(genre_idx, QSqlRelation("genres", "id", "name"))
# Set the localized header captions:
- model.setHeaderData(author_idx, Qt.Horizontal, self.tr("Author Name"))
- model.setHeaderData(genre_idx, Qt.Horizontal, self.tr("Genre"))
- model.setHeaderData(model.fieldIndex("title"), Qt.Horizontal, self.tr("Title"))
- model.setHeaderData(model.fieldIndex("year"), Qt.Horizontal, self.tr("Year"))
- model.setHeaderData(model.fieldIndex("rating"), Qt.Horizontal, self.tr("Rating"))
+ model.setHeaderData(author_idx, Qt.Orientation.Horizontal, self.tr("Author Name"))
+ model.setHeaderData(genre_idx, Qt.Orientation.Horizontal, self.tr("Genre"))
+ model.setHeaderData(model.fieldIndex("title"), Qt.Orientation.Horizontal, self.tr("Title"))
+ model.setHeaderData(model.fieldIndex("year"), Qt.Orientation.Horizontal, self.tr("Year"))
+ model.setHeaderData(model.fieldIndex("rating"),
+ Qt.Orientation.Horizontal, self.tr("Rating"))
if not model.select():
print(model.lastError())
<item>
<widget class="QTableView" name="bookTable">
<property name="selectionBehavior">
- <enum>QAbstractItemView::SelectRows</enum>
+ <enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
</property>
</widget>
</item>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtSql import QSqlDatabase, QSqlQuery
from datetime import date
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtWidgets import QApplication
################################################################################
## Form generated from reading UI file 'bookwindow.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.vboxLayout1.setContentsMargins(9, 9, 9, 9)
self.bookTable = QTableView(self.groupBox)
self.bookTable.setObjectName(u"bookTable")
- self.bookTable.setSelectionBehavior(QAbstractItemView.SelectRows)
+ self.bookTable.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.vboxLayout1.addWidget(self.bookTable)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the relationaltablemodel example from Qt v6.x"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the relationaltablemodel example from Qt v6.x"""
model.setEditStrategy(QSqlRelationalTableModel.OnManualSubmit)
model.setRelation(2, QSqlRelation("city", "id", "name"))
model.setRelation(3, QSqlRelation("country", "id", "name"))
- model.setHeaderData(0, Qt.Horizontal, QObject().tr("ID"))
+ model.setHeaderData(0, Qt.Orientation.Horizontal, QObject().tr("ID"))
- model.setHeaderData(1, Qt.Horizontal, QObject().tr("Name"))
- model.setHeaderData(2, Qt.Horizontal, QObject().tr("City"))
- model.setHeaderData(3, Qt.Horizontal, QObject().tr("Country"))
+ model.setHeaderData(1, Qt.Orientation.Horizontal, QObject().tr("Name"))
+ model.setHeaderData(2, Qt.Orientation.Horizontal, QObject().tr("City"))
+ model.setHeaderData(3, Qt.Orientation.Horizontal, QObject().tr("Country"))
model.select()
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the examples/statemachine/moveblocks example from Qt v6.x"""
def paint(self, painter: QPainter,
option: QStyleOptionGraphicsItem, widget: QWidget | None = None):
- painter.fillRect(self.rect(), Qt.blue)
+ painter.fillRect(self.rect(), Qt.GlobalColor.blue)
class StateSwitchTransition(QAbstractTransition):
button4.setZValue(3)
scene = QGraphicsScene(0, 0, 300, 300)
- scene.setBackgroundBrush(Qt.black)
+ scene.setBackgroundBrush(Qt.GlobalColor.black)
scene.addItem(button1)
scene.addItem(button2)
scene.addItem(button3)
window = GraphicsView(scene)
window.setFrameStyle(0)
- window.setAlignment(Qt.AlignLeft | Qt.AlignTop)
- window.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
- window.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ window.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
+ window.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
+ window.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
machine = QStateMachine()
anim = QPropertyAnimation(button4, b"geometry")
anim.setDuration(1000)
- anim.setEasingCurve(QEasingCurve.OutElastic)
+ anim.setEasingCurve(QEasingCurve.Type.OutElastic)
animation_group.addAnimation(anim)
sub_group = QSequentialAnimationGroup(animation_group)
sub_group.addPause(100)
anim = QPropertyAnimation(button3, b"geometry")
anim.setDuration(1000)
- anim.setEasingCurve(QEasingCurve.OutElastic)
+ anim.setEasingCurve(QEasingCurve.Type.OutElastic)
sub_group.addAnimation(anim)
sub_group = QSequentialAnimationGroup(animation_group)
sub_group.addPause(150)
anim = QPropertyAnimation(button2, b"geometry")
anim.setDuration(1000)
- anim.setEasingCurve(QEasingCurve.OutElastic)
+ anim.setEasingCurve(QEasingCurve.Type.OutElastic)
sub_group.addAnimation(anim)
sub_group = QSequentialAnimationGroup(animation_group)
sub_group.addPause(200)
anim = QPropertyAnimation(button1, b"geometry")
anim.setDuration(1000)
- anim.setEasingCurve(QEasingCurve.OutElastic)
+ anim.setEasingCurve(QEasingCurve.Type.OutElastic)
sub_group.addAnimation(anim)
state_switcher = StateSwitcher(machine)
--- /dev/null
+StateMachine Ping Pong Example
+==============================
+
+The Ping Pong States example shows how to use parallel states together
+with custom events and transitions in The State Machine Framework.
+It corresponds to the Qt example `Ping Pong States Example
+<https://doc.qt.io/qt-5/qtwidgets-statemachine-pingpong-example.html>`_.
+
+.. image:: pingpong.png
+ :align: center
+ :alt: ping_pong screenshot
+ :width: 400
# Copyright (C) 2010 velociraptor Genjix <aphidia@hotmail.com>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
--- /dev/null
+StateMachine Rogue Example
+==========================
+
+The Rogue example shows how to use the Qt state machine for event handling
+It corresponds to the Qt example `Rogue Example
+<https://doc.qt.io/qt-5/qtwidgets-statemachine-rogue-example.html>`_.
+
+.. image:: rogue.png
+ :align: center
+ :alt: rogue screenshot
+ :width: 400
# Copyright (C) 2010 velociraptor Genjix <aphidia@hotmail.com>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
class MovementTransition(QEventTransition):
def __init__(self, window):
- super().__init__(window, QEvent.KeyPress)
+ super().__init__(window, QEvent.Type.KeyPress)
self.window = window
def eventTest(self, event):
- if (event.type() == QEvent.StateMachineWrapped
- and event.event().type() == QEvent.KeyPress):
+ if (event.type() == QEvent.Type.StateMachineWrapped
+ and event.event().type() == QEvent.Type.KeyPress):
key = event.event().key()
- return (key == Qt.Key_2 or key == Qt.Key_8
- or key == Qt.Key_6 or key == Qt.Key_4)
+ return (key == Qt.Key.Key_2 or key == Qt.Key.Key_8
+ or key == Qt.Key.Key_6 or key == Qt.Key.Key_4)
return False
def onTransition(self, event):
key = event.event().key()
- if key == Qt.Key_4:
+ if key == Qt.Key.Key_4:
self.window.move_player(self.window.left)
- if key == Qt.Key_8:
+ if key == Qt.Key.Key_8:
self.window.move_player(self.window.Up)
- if key == Qt.Key_6:
+ if key == Qt.Key.Key_6:
self.window.move_player(self.window.right)
- if key == Qt.Key_2:
+ if key == Qt.Key.Key_2:
self.window.move_player(self.window.down)
quit_state = QState(machine)
quit_state.assignProperty(self, 'status', 'Really quit(y/n)?')
- yes_transition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Y)
+ yes_transition = QKeyEventTransition(self, QEvent.Type.KeyPress, Qt.Key.Key_Y)
self._final_state = QFinalState(machine)
yes_transition.setTargetState(self._final_state)
quit_state.addTransition(yes_transition)
- no_transition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_N)
+ no_transition = QKeyEventTransition(self, QEvent.Type.KeyPress, Qt.Key.Key_N)
no_transition.setTargetState(input_state)
quit_state.addTransition(no_transition)
- quit_transition = QKeyEventTransition(self, QEvent.KeyPress, Qt.Key_Q)
+ quit_transition = QKeyEventTransition(self, QEvent.Type.KeyPress, Qt.Key.Key_Q)
quit_transition.setTargetState(quit_state)
input_state.addTransition(quit_transition)
font_height = metrics.height()
font_width = metrics.horizontalAdvance('X')
- painter.fillRect(self.rect(), Qt.black)
- painter.setPen(Qt.white)
+ painter.fillRect(self.rect(), Qt.GlobalColor.black)
+ painter.setPen(Qt.GlobalColor.white)
y_pos = font_height
painter.drawText(QPoint(0, y_pos), self.status)
# Copyright (C) 2010 velociraptor Genjix <aphidia@hotmail.com>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
if not self._on_val:
return
with QPainter(self) as painter:
- painter.setRenderHint(QPainter.Antialiasing)
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
painter.setBrush(self.color)
painter.drawEllipse(0, 0, self.width(), self.height())
def __init__(self):
super().__init__()
vbox = QVBoxLayout(self)
- self._red_light = LightWidget(Qt.red)
+ self._red_light = LightWidget(Qt.GlobalColor.red)
vbox.addWidget(self._red_light)
- self._yellow_light = LightWidget(Qt.yellow)
+ self._yellow_light = LightWidget(Qt.GlobalColor.yellow)
vbox.addWidget(self._yellow_light)
- self._green_light = LightWidget(Qt.green)
+ self._green_light = LightWidget(Qt.GlobalColor.green)
vbox.addWidget(self._green_light)
pal = QPalette()
- pal.setColor(QPalette.Window, Qt.black)
+ pal.setColor(QPalette.ColorRole.Window, Qt.GlobalColor.black)
self.setPalette(pal)
self.setAutoFillBackground(True)
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Dialog {
+ id: dialog
+
+ signal finished(string itemName, string category, real cost, string date)
+
+ contentItem: ColumnLayout {
+ id: form
+ spacing: 10
+ property alias itemName: itemName
+ property alias category: category
+ property alias cost: cost
+ property alias date: date
+
+ GridLayout {
+ columns: 2
+ columnSpacing: 20
+ rowSpacing: 10
+ Layout.fillWidth: true
+
+ Label {
+ text: qsTr("Item Name:")
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ TextField {
+ id: itemName
+ focus: true
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ Label {
+ text: qsTr("Category:")
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ TextField {
+ id: category
+ focus: true
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ Label {
+ text: qsTr("Cost:")
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ TextField {
+ id: cost
+ focus: true
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ placeholderText: qsTr("€")
+ inputMethodHints: Qt.ImhFormattedNumbersOnly
+ }
+
+ Label {
+ text: qsTr("Date:")
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ TextField {
+ id: date
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ // placeholderText: qsTr("dd-mm-yyyy")
+ validator: RegularExpressionValidator { regularExpression: /^[0-3]?\d-[01]?\d-\d{4}$/ }
+ // code to add the - automatically
+ onTextChanged: {
+ if (date.text.length === 2 || date.text.length === 5) {
+ date.text += "-"
+ }
+ }
+ Component.onCompleted: {
+ var today = new Date();
+ var day = String(today.getDate()).padStart(2, '0');
+ var month = String(today.getMonth() + 1).padStart(2, '0'); // Months are zero-based
+ var year = today.getFullYear();
+ date.placeholderText = day + "-" + month + "-" + year;
+ }
+ }
+ }
+ }
+
+ function createEntry() {
+ form.itemName.clear()
+ form.category.clear()
+ form.cost.clear()
+ form.date.clear()
+ dialog.title = qsTr("Add Finance Item")
+ dialog.open()
+ }
+
+ x: parent.width / 2 - width / 2
+ y: parent.height / 2 - height / 2
+
+ focus: true
+ modal: true
+ title: qsTr("Add Finance Item")
+ standardButtons: Dialog.Ok | Dialog.Cancel
+
+ Component.onCompleted: {
+ dialog.visible = false
+ Qt.inputMethod.visibleChanged.connect(adjustDialogPosition)
+ }
+
+ function adjustDialogPosition() {
+ if (Qt.inputMethod.visible) {
+ // If the keyboard is visible, move the dialog up
+ dialog.y = parent.height / 4 - height / 2
+ } else {
+ // If the keyboard is not visible, center the dialog
+ dialog.y = parent.height / 2 - height / 2
+ }
+ }
+
+ onAccepted: {
+ finished(form.itemName.text, form.category.text, parseFloat(form.cost.text), form.date.text)
+ }
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+ItemDelegate {
+ id: delegate
+ checkable: true
+ width: parent.width
+ height: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.15 :
+ Math.min(window.width, window.height) * 0.1
+
+ contentItem:
+ RowLayout {
+ Label {
+ id: dateLabel
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.03 :
+ Math.min(window.width, window.height) * 0.02
+ text: date
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ Layout.preferredWidth: 1
+ color: Material.primaryTextColor
+ }
+
+ ColumnLayout {
+ spacing: 5
+ Layout.fillWidth: true
+ Layout.preferredWidth: 1
+
+ Label {
+ text: item_name
+ color: "#5c8540"
+ font.bold: true
+ elide: Text.ElideRight
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.03 :
+ Math.min(window.width, window.height) * 0.02
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: category
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.03 :
+ Math.min(window.width, window.height) * 0.02
+ }
+ }
+
+ Item {
+ Layout.fillWidth: true // This item will take up the remaining space
+ }
+
+ ColumnLayout {
+ spacing: 5
+ Layout.fillWidth: true
+ Layout.preferredWidth: 1
+
+ Label {
+ text: "you spent:"
+ color: "#5c8540"
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.03 :
+ Math.min(window.width, window.height) * 0.02
+ }
+
+ Label {
+ text: cost + "€"
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.03 :
+ Math.min(window.width, window.height) * 0.02
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+import QtQuick
+import QtGraphs
+import QtQuick.Controls.Material
+
+Item {
+ width: Screen.width
+ height: Screen.height
+
+ GraphsView {
+ id: chart
+ anchors.fill: parent
+ antialiasing: true
+
+ theme: GraphsTheme {
+ colorScheme: Qt.Dark
+ theme: GraphsTheme.Theme.QtGreenNeon
+ }
+
+ PieSeries {
+ id: pieSeries
+ }
+ }
+
+ Text {
+ id: chartTitle
+ text: "Total Expenses Breakdown by Category"
+ color: "#5c8540"
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.04 :
+ Math.min(window.width, window.height) * 0.03
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: parent.top
+ anchors.topMargin: 20
+ }
+
+ function updateChart(data) {
+ pieSeries.clear()
+ for (var category in data) {
+ var slice = pieSeries.append(category, data[category])
+ slice.label = category + ": " + data[category] + "€"
+ slice.labelVisible = true
+ }
+ }
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+ListView {
+ id: listView
+ anchors.fill: parent
+ height: parent.height
+ property var financeModel
+
+ delegate: FinanceDelegate {
+ id: delegate
+ width: listView.width
+ }
+
+ model: financeModel
+
+ section.property: "month" // Group items by the "month" property
+ section.criteria: ViewSection.FullString
+ section.delegate: Component {
+ id: sectionHeading
+ Rectangle {
+ width: listView.width
+ height: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.05 :
+ Math.min(window.width, window.height) * 0.03
+ color: "#5c8540"
+
+ required property string section
+
+ Text {
+ text: parent.section
+ font.bold: true
+ // depending on the screen density, adjust the font size
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.03 :
+ Math.min(window.width, window.height) * 0.02
+ color: Material.primaryTextColor
+ }
+ }
+ }
+
+ ScrollBar.vertical: ScrollBar { }
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Controls.Material
+import Finance
+
+ApplicationWindow {
+ id: window
+ Material.theme: Material.Dark
+ Material.accent: Material.Gray
+ width: Screen.width * 0.3
+ height: Screen.height * 0.5
+ visible: true
+ title: qsTr("Finance Manager")
+
+ // Add a toolbar for the application, only visible on mobile
+ header: ToolBar {
+ Material.primary: "#5c8540"
+ visible: Qt.platform.os == "android"
+ RowLayout {
+ anchors.fill: parent
+ Label {
+ text: qsTr("Finance Manager")
+ font.pixelSize: 20
+ Layout.alignment: Qt.AlignCenter
+ }
+ }
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ TabBar {
+ id: tabBar
+ Layout.fillWidth: true
+
+ TabButton {
+ text: qsTr("Expenses")
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.04 :
+ Math.min(window.width, window.height) * 0.02
+ onClicked: stackView.currentIndex = 0
+ }
+
+ TabButton {
+ text: qsTr("Charts")
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.04 :
+ Math.min(window.width, window.height) * 0.02
+ onClicked: stackView.currentIndex = 1
+ }
+ }
+
+ StackLayout {
+ id: stackView
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ Item {
+ id: expensesView
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ FinanceView {
+ id: financeView
+ anchors.fill: parent
+ financeModel: finance_model
+ }
+ }
+
+ Item {
+ id: chartsView
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ FinancePieChart {
+ id: financePieChart
+ anchors.fill: parent
+ Component.onCompleted: {
+ var categoryData = finance_model.getCategoryData()
+ updateChart(categoryData)
+ }
+ }
+ }
+ }
+ }
+
+ // Model to store the finance data. Created from Python.
+ FinanceModel {
+ id: finance_model
+ }
+
+ // Add a dialog to add new entries
+ AddDialog {
+ id: addDialog
+ onFinished: function(item_name, category, cost, date) {
+ finance_model.append(item_name, category, cost, date)
+ var categoryData = finance_model.getCategoryData()
+ financePieChart.updateChart(categoryData)
+ }
+ }
+
+ // Add a button to open the dialog
+ ToolButton {
+ id: roundButton
+ text: qsTr("+")
+ highlighted: true
+ Material.elevation: 6
+ width: Qt.platform.os === "android" ?
+ Math.min(parent.width * 0.2, Screen.width * 0.15) :
+ Math.min(parent.width * 0.060, Screen.width * 0.05)
+ height: width // Keep the button circular
+ anchors.margins: 10
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ background: Rectangle {
+ color: "#5c8540"
+ radius: roundButton.width / 2
+ }
+ font.pixelSize: width * 0.4
+ onClicked: {
+ addDialog.createEntry()
+ }
+ }
+}
--- /dev/null
+module Finance
+Main 1.0 Main.qml
+FinanceView 1.0 FinanceView.qml
+FinancePieChart 1.0 FinancePieChart.qml
+FinanceDelegate 1.0 FinanceDelegate.qml
+AddDialog 1.0 AddDialog.qml
--- /dev/null
+# Finance Manager Example - Part 1
+
+This example represents the part one of the tutorial series on creating a simple Finance Manager
+that allows users to manage their expenses and visualize them using a pie chart, using PySide6,
+[SQLAlchemy], [FastAPI], and [Pydantic].
+
+For more details, see the [Finance Manager Tutorial - Part 1](tutorial_financemanager_partone).
+
+[SQLalchemy]: https://www.sqlalchemy.org/
+[FastApi]: https://fastapi.tiangolo.com/
+[PyDantic]: https://pydantic-docs.helpmanual.io/
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from datetime import datetime
+from dataclasses import dataclass
+from enum import IntEnum
+from collections import defaultdict
+
+from PySide6.QtCore import (QAbstractListModel, QEnum, Qt, QModelIndex, Slot,
+ QByteArray)
+from PySide6.QtQml import QmlElement
+
+QML_IMPORT_NAME = "Finance"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class FinanceModel(QAbstractListModel):
+
+ @QEnum
+ class FinanceRole(IntEnum):
+ ItemNameRole = Qt.ItemDataRole.DisplayRole
+ CategoryRole = Qt.ItemDataRole.UserRole
+ CostRole = Qt.ItemDataRole.UserRole + 1
+ DateRole = Qt.ItemDataRole.UserRole + 2
+ MonthRole = Qt.ItemDataRole.UserRole + 3
+
+ @dataclass
+ class Finance:
+ item_name: str
+ category: str
+ cost: float
+ date: str
+
+ @property
+ def month(self):
+ return datetime.strptime(self.date, "%d-%m-%Y").strftime("%B %Y")
+
+ def __init__(self, parent=None) -> None:
+ super().__init__(parent)
+ self.m_finances = []
+ self.m_finances.append(self.Finance("Mobile Prepaid", "Electronics", 20.00, "15-02-2024"))
+ self.m_finances.append(self.Finance("Groceries-Feb-Week1", "Groceries", 60.75,
+ "16-01-2024"))
+ self.m_finances.append(self.Finance("Bus Ticket", "Transport", 5.50, "17-01-2024"))
+ self.m_finances.append(self.Finance("Book", "Education", 25.00, "18-01-2024"))
+
+ def rowCount(self, parent=QModelIndex()):
+ return len(self.m_finances)
+
+ def data(self, index: QModelIndex, role: int):
+ row = index.row()
+ if row < self.rowCount():
+ finance = self.m_finances[row]
+ if role == FinanceModel.FinanceRole.ItemNameRole:
+ return finance.item_name
+ if role == FinanceModel.FinanceRole.CategoryRole:
+ return finance.category
+ if role == FinanceModel.FinanceRole.CostRole:
+ return finance.cost
+ if role == FinanceModel.FinanceRole.DateRole:
+ return finance.date
+ if role == FinanceModel.FinanceRole.MonthRole:
+ return finance.month
+ return None
+
+ @Slot(result=dict)
+ def getCategoryData(self):
+ category_data = defaultdict(float)
+ for finance in self.m_finances:
+ category_data[finance.category] += finance.cost
+ return dict(category_data)
+
+ def roleNames(self):
+ roles = super().roleNames()
+ roles[FinanceModel.FinanceRole.ItemNameRole] = QByteArray(b"item_name")
+ roles[FinanceModel.FinanceRole.CategoryRole] = QByteArray(b"category")
+ roles[FinanceModel.FinanceRole.CostRole] = QByteArray(b"cost")
+ roles[FinanceModel.FinanceRole.DateRole] = QByteArray(b"date")
+ roles[FinanceModel.FinanceRole.MonthRole] = QByteArray(b"month")
+ return roles
+
+ @Slot(int, result='QVariantMap')
+ def get(self, row: int):
+ finance = self.m_finances[row]
+ return {"item_name": finance.item_name, "category": finance.category,
+ "cost": finance.cost, "date": finance.date}
+
+ @Slot(str, str, float, str)
+ def append(self, item_name: str, category: str, cost: float, date: str):
+ finance = self.Finance(item_name, category, cost, date)
+ self.beginInsertRows(QModelIndex(), 0, 0) # Insert at the front
+ self.m_finances.insert(0, finance) # Insert at the front of the list
+ self.endInsertRows()
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from pathlib import Path
+from PySide6.QtWidgets import QApplication
+from PySide6.QtQml import QQmlApplicationEngine
+
+from financemodel import FinanceModel # noqa: F401
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ QApplication.setOrganizationName("QtProject")
+ QApplication.setApplicationName("Finance Manager")
+ engine = QQmlApplicationEngine()
+
+ engine.addImportPath(Path(__file__).parent)
+ engine.loadFromModule("Finance", "Main")
+
+ if not engine.rootObjects():
+ sys.exit(-1)
+
+ exit_code = app.exec()
+ del engine
+ sys.exit(exit_code)
--- /dev/null
+{
+ "files": [
+ "main.py",
+ "financemodel.py",
+ "Finance/AddDialog.qml",
+ "Finance/FinanceDelegate.qml",
+ "Finance/FinancePieChart.qml",
+ "Finance/FinanceView.qml",
+ "Finance/Main.qml",
+ "Finance/qmldir"
+ ]
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Dialog {
+ id: dialog
+
+ signal finished(string itemName, string category, real cost, string date)
+
+ contentItem: ColumnLayout {
+ id: form
+ spacing: 10
+ property alias itemName: itemName
+ property alias category: category
+ property alias cost: cost
+ property alias date: date
+
+ GridLayout {
+ columns: 2
+ columnSpacing: 20
+ rowSpacing: 10
+ Layout.fillWidth: true
+
+ Label {
+ text: qsTr("Item Name:")
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ TextField {
+ id: itemName
+ focus: true
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ Label {
+ text: qsTr("Category:")
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ TextField {
+ id: category
+ focus: true
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ Label {
+ text: qsTr("Cost:")
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ TextField {
+ id: cost
+ focus: true
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ placeholderText: qsTr("€")
+ inputMethodHints: Qt.ImhFormattedNumbersOnly
+ }
+
+ Label {
+ text: qsTr("Date:")
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ TextField {
+ id: date
+ focus: true
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ placeholderText: qsTr("dd-mm-yyyy")
+ validator: RegularExpressionValidator { regularExpression: /^[0-3]?\d-[01]?\d-\d{4}$/ }
+ // code to add the - automatically
+ onTextChanged: {
+ if (date.text.length === 2 || date.text.length === 5) {
+ date.text += "-"
+ }
+ }
+ Component.onCompleted: {
+ var today = new Date();
+ var day = String(today.getDate()).padStart(2, '0');
+ var month = String(today.getMonth() + 1).padStart(2, '0'); // Months are zero-based
+ var year = today.getFullYear();
+ date.placeholderText = day + "-" + month + "-" + year;
+ }
+ }
+ }
+ }
+
+ function createEntry() {
+ form.itemName.clear()
+ form.category.clear()
+ form.cost.clear()
+ form.date.clear()
+ dialog.title = qsTr("Add Finance Item")
+ dialog.open()
+ }
+
+ x: parent.width / 2 - width / 2
+ y: parent.height / 2 - height / 2
+
+ focus: true
+ modal: true
+ title: qsTr("Add Finance Item")
+ standardButtons: Dialog.Ok | Dialog.Cancel
+
+ Component.onCompleted: {
+ dialog.visible = false
+ Qt.inputMethod.visibleChanged.connect(adjustDialogPosition)
+ }
+
+ function adjustDialogPosition() {
+ if (Qt.inputMethod.visible) {
+ // If the keyboard is visible, move the dialog up
+ dialog.y = parent.height / 4 - height / 2
+ } else {
+ // If the keyboard is not visible, center the dialog
+ dialog.y = parent.height / 2 - height / 2
+ }
+ }
+
+ onAccepted: {
+ finished(form.itemName.text, form.category.text, parseFloat(form.cost.text), form.date.text)
+ }
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+ItemDelegate {
+ id: delegate
+ checkable: true
+ width: parent.width
+ height: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.15 :
+ Math.min(window.width, window.height) * 0.1
+
+ contentItem:
+ RowLayout {
+ Label {
+ id: dateLabel
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.03 :
+ Math.min(window.width, window.height) * 0.02
+ text: date
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ Layout.preferredWidth: 1
+ color: Material.primaryTextColor
+ }
+
+ ColumnLayout {
+ spacing: 5
+ Layout.fillWidth: true
+ Layout.preferredWidth: 1
+
+ Label {
+ text: item_name
+ color: "#5c8540"
+ font.bold: true
+ elide: Text.ElideRight
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.04 :
+ Math.min(window.width, window.height) * 0.02
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: category
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.03 :
+ Math.min(window.width, window.height) * 0.02
+ }
+ }
+
+ Item {
+ Layout.fillWidth: true // This item will take up the remaining space
+ }
+
+ ColumnLayout {
+ spacing: 5
+ Layout.fillWidth: true
+ Layout.preferredWidth: 1
+
+ Label {
+ text: "you spent:"
+ color: "#5c8540"
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.03 :
+ Math.min(window.width, window.height) * 0.02
+ }
+
+ Label {
+ text: cost + "€"
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.03 :
+ Math.min(window.width, window.height) * 0.02
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtGraphs
+import QtQuick.Controls.Material
+
+Item {
+ width: Screen.width
+ height: Screen.height
+
+ GraphsView {
+ id: chart
+ anchors.fill: parent
+ antialiasing: true
+
+ theme: GraphsTheme {
+ colorScheme: Qt.Dark
+ theme: GraphsTheme.Theme.QtGreenNeon
+ }
+
+ PieSeries {
+ id: pieSeries
+ }
+ }
+
+ Text {
+ id: chartTitle
+ text: "Total Expenses Breakdown by Category"
+ color: "#5c8540"
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.04 :
+ Math.min(window.width, window.height) * 0.03
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: parent.top
+ anchors.topMargin: 20
+ }
+
+ function updateChart(data) {
+ pieSeries.clear()
+ for (var category in data) {
+ var slice = pieSeries.append(category, data[category])
+ slice.label = category + ": " + data[category] + "€"
+ slice.labelVisible = true
+ }
+ }
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+ListView {
+ id: listView
+ anchors.fill: parent
+ height: parent.height
+ property var financeModel
+
+ delegate: FinanceDelegate {
+ id: delegate
+ width: listView.width
+ }
+
+ model: financeModel
+
+ section.property: "month" // Group items by the "month" property
+ section.criteria: ViewSection.FullString
+ section.delegate: Component {
+ id: sectionHeading
+ Rectangle {
+ width: listView.width
+ height: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.05 :
+ Math.min(window.width, window.height) * 0.03
+ color: "#5c8540"
+
+ required property string section
+
+ Text {
+ text: parent.section
+ font.bold: true
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.03 :
+ Math.min(window.width, window.height) * 0.02
+ color: Material.primaryTextColor
+ }
+ }
+ }
+
+ ScrollBar.vertical: ScrollBar { }
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Controls.Material
+import Finance
+
+ApplicationWindow {
+ id: window
+ Material.theme: Material.Dark
+ Material.accent: Material.Gray
+ width: Screen.width * 0.3
+ height: Screen.height * 0.5
+ visible: true
+ title: qsTr("Finance Manager")
+
+ // Add a toolbar for the application, only visible on mobile
+ header: ToolBar {
+ Material.primary: "#5c8540"
+ visible: Qt.platform.os == "android"
+ RowLayout {
+ anchors.fill: parent
+ Label {
+ text: qsTr("Finance Manager")
+ font.pixelSize: 20
+ Layout.alignment: Qt.AlignCenter
+ }
+ }
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ TabBar {
+ id: tabBar
+ Layout.fillWidth: true
+
+ TabButton {
+ text: qsTr("Expenses")
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.04 :
+ Math.min(window.width, window.height) * 0.02
+ onClicked: stackView.currentIndex = 0
+ }
+
+ TabButton {
+ text: qsTr("Charts")
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.04 :
+ Math.min(window.width, window.height) * 0.02
+ onClicked: stackView.currentIndex = 1
+ }
+ }
+
+ StackLayout {
+ id: stackView
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ Item {
+ id: expensesView
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ FinanceView {
+ id: financeView
+ anchors.fill: parent
+ financeModel: finance_model
+ }
+ }
+
+ Item {
+ id: chartsView
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ FinancePieChart {
+ id: financePieChart
+ anchors.fill: parent
+ Component.onCompleted: {
+ var categoryData = finance_model.getCategoryData()
+ updateChart(categoryData)
+ }
+ }
+ }
+ }
+ }
+
+ // Model to store the finance data. Created from Python.
+ FinanceModel {
+ id: finance_model
+ }
+
+ // Add a dialog to add new entries
+ AddDialog {
+ id: addDialog
+ onFinished: function(item_name, category, cost, date) {
+ finance_model.append(item_name, category, cost, date)
+ var categoryData = finance_model.getCategoryData()
+ financePieChart.updateChart(categoryData)
+ }
+ }
+
+ // Add a button to open the dialog
+ ToolButton {
+ id: roundButton
+ text: qsTr("+")
+ highlighted: true
+ Material.elevation: 6
+ width: Qt.platform.os === "android" ?
+ Math.min(parent.width * 0.2, Screen.width * 0.15) :
+ Math.min(parent.width * 0.060, Screen.width * 0.05)
+ height: width // Keep the button circular
+ anchors.margins: 10
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ background: Rectangle {
+ color: "#5c8540"
+ radius: roundButton.width / 2
+ }
+ font.pixelSize: width * 0.4
+ onClicked: {
+ addDialog.createEntry()
+ }
+ }
+}
--- /dev/null
+module Finance
+Main 1.0 Main.qml
+FinanceView 1.0 FinanceView.qml
+FinancePieChart 1.0 FinancePieChart.qml
+FinanceDelegate 1.0 FinanceDelegate.qml
+AddDialog 1.0 AddDialog.qml
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from sqlalchemy import create_engine, Column, Integer, String, Float
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import sessionmaker
+import os
+import platform
+from pathlib import Path
+
+Base = declarative_base()
+
+
+class Finance(Base):
+ __tablename__ = 'finances'
+ id = Column(Integer, primary_key=True)
+ item_name = Column(String)
+ category = Column(String)
+ cost = Column(Float)
+ date = Column(String)
+
+
+# Check for an environment variable for the database path
+env_db_path = os.getenv('FINANCE_MANAGER_DB_PATH')
+
+if env_db_path:
+ db_path = Path(env_db_path)
+else:
+ # Determine the application data directory based on the operating system using pathlib
+ if platform.system() == 'Windows':
+ app_data_location = Path(os.getenv('APPDATA')) / 'FinanceManager'
+ elif platform.system() == 'Darwin': # macOS
+ app_data_location = Path.home() / 'Library' / 'Application Support' / 'FinanceManager'
+ else: # Linux and other Unix-like systems
+ app_data_location = Path.home() / '.local' / 'share' / 'FinanceManager'
+
+ db_path = app_data_location / 'finances.db'
+
+DATABASE_URL = f'sqlite:///{db_path}'
+engine = create_engine(DATABASE_URL)
+Session = sessionmaker(bind=engine)
+
+# Default data to be added to the database
+default_data = [
+ {"item_name": "Mobile Prepaid", "category": "Electronics", "cost": 20.00, "date": "15-02-2024"},
+ {"item_name": "Groceries-Feb-Week1", "category": "Groceries", "cost": 60.75,
+ "date": "16-01-2024"},
+ {"item_name": "Bus Ticket", "category": "Transport", "cost": 5.50, "date": "17-01-2024"},
+ {"item_name": "Book", "category": "Education", "cost": 25.00, "date": "18-01-2024"},
+]
+
+
+def initialize_database():
+ if db_path.exists():
+ print(f"Database '{db_path}' already exists.")
+ return
+
+ app_data_location.mkdir(parents=True, exist_ok=True)
+ Base.metadata.create_all(engine)
+ print(f"Database '{db_path}' created successfully.")
+ session = Session()
+
+ for data in default_data:
+ finance = Finance(**data)
+ session.add(finance)
+
+ session.commit()
+ print("Default data has been added to the database.")
--- /dev/null
+# Finance Manager Example - Part 2
+
+This example represents the part two of the tutorial series on creating a simple Finance Manager
+that allows users to manage their expenses and visualize them using a pie chart, using PySide6,
+[SQLAlchemy], [FastAPI], and [Pydantic].
+
+For more details, see the [Finance Manager Tutorial - Part 2](tutorial_financemanager_parttwo).
+
+[SQLalchemy]: https://www.sqlalchemy.org/
+[FastApi]: https://fastapi.tiangolo.com/
+[PyDantic]: https://pydantic-docs.helpmanual.io/
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from datetime import datetime
+from dataclasses import dataclass
+from enum import IntEnum
+from collections import defaultdict
+
+from PySide6.QtCore import (QAbstractListModel, QEnum, Qt, QModelIndex, Slot,
+ QByteArray)
+from PySide6.QtQml import QmlElement
+import database
+
+QML_IMPORT_NAME = "Finance"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class FinanceModel(QAbstractListModel):
+
+ @QEnum
+ class FinanceRole(IntEnum):
+ ItemNameRole = Qt.ItemDataRole.DisplayRole
+ CategoryRole = Qt.ItemDataRole.UserRole
+ CostRole = Qt.ItemDataRole.UserRole + 1
+ DateRole = Qt.ItemDataRole.UserRole + 2
+ MonthRole = Qt.ItemDataRole.UserRole + 3
+
+ @dataclass
+ class Finance:
+ item_name: str
+ category: str
+ cost: float
+ date: str
+
+ @property
+ def month(self):
+ return datetime.strptime(self.date, "%d-%m-%Y").strftime("%B %Y")
+
+ def __init__(self, parent=None) -> None:
+ super().__init__(parent)
+ self.session = database.Session()
+ self.m_finances = self.load_finances()
+
+ def load_finances(self):
+ finances = []
+ for finance in self.session.query(database.Finance).all():
+ finances.append(self.Finance(finance.item_name, finance.category, finance.cost,
+ finance.date))
+ return finances
+
+ def rowCount(self, parent=QModelIndex()):
+ return len(self.m_finances)
+
+ def data(self, index: QModelIndex, role: int):
+ row = index.row()
+ if row < self.rowCount():
+ finance = self.m_finances[row]
+ if role == FinanceModel.FinanceRole.ItemNameRole:
+ return finance.item_name
+ if role == FinanceModel.FinanceRole.CategoryRole:
+ return finance.category
+ if role == FinanceModel.FinanceRole.CostRole:
+ return finance.cost
+ if role == FinanceModel.FinanceRole.DateRole:
+ return finance.date
+ if role == FinanceModel.FinanceRole.MonthRole:
+ return finance.month
+ return None
+
+ @Slot(result=dict)
+ def getCategoryData(self):
+ category_data = defaultdict(float)
+ for finance in self.m_finances:
+ category_data[finance.category] += finance.cost
+ return dict(category_data)
+
+ def roleNames(self):
+ roles = super().roleNames()
+ roles[FinanceModel.FinanceRole.ItemNameRole] = QByteArray(b"item_name")
+ roles[FinanceModel.FinanceRole.CategoryRole] = QByteArray(b"category")
+ roles[FinanceModel.FinanceRole.CostRole] = QByteArray(b"cost")
+ roles[FinanceModel.FinanceRole.DateRole] = QByteArray(b"date")
+ roles[FinanceModel.FinanceRole.MonthRole] = QByteArray(b"month")
+ return roles
+
+ @Slot(int, result='QVariantMap')
+ def get(self, row: int):
+ finance = self.m_finances[row]
+ return {"item_name": finance.item_name, "category": finance.category,
+ "cost": finance.cost, "date": finance.date}
+
+ @Slot(str, str, float, str)
+ def append(self, item_name: str, category: str, cost: float, date: str):
+ finance = self.Finance(item_name, category, cost, date)
+ self.session.add(database.Finance(item_name=item_name, category=category, cost=cost,
+ date=date))
+ self.beginInsertRows(QModelIndex(), 0, 0) # Insert at the front
+ self.m_finances.insert(0, finance) # Insert at the front of the list
+ self.endInsertRows()
+ self.session.commit()
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from pathlib import Path
+
+from PySide6.QtWidgets import QApplication
+from PySide6.QtQml import QQmlApplicationEngine
+
+from financemodel import FinanceModel # noqa: F401
+from database import initialize_database
+
+if __name__ == '__main__':
+ # Initialize the database if it does not exist
+ initialize_database()
+
+ app = QApplication(sys.argv)
+ QApplication.setOrganizationName("QtProject")
+ QApplication.setApplicationName("Finance Manager")
+ engine = QQmlApplicationEngine()
+
+ engine.addImportPath(Path(__file__).parent)
+ engine.loadFromModule("Finance", "Main")
+
+ if not engine.rootObjects():
+ sys.exit(-1)
+
+ exit_code = app.exec()
+ del engine
+ sys.exit(exit_code)
--- /dev/null
+{
+ "files": [
+ "database.py",
+ "main.py",
+ "financemodel.py",
+ "Finance/AddDialog.qml",
+ "Finance/FinanceDelegate.qml",
+ "Finance/FinancePieChart.qml",
+ "Finance/FinanceView.qml",
+ "Finance/Main.qml",
+ "Finance/qmldir",
+ "requirements.txt"
+ ]
+}
--- /dev/null
+sqlalchemy
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+from sqlalchemy import create_engine, Column, Integer, String, Float
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import sessionmaker
+import os
+import platform
+from pathlib import Path
+
+Base = declarative_base()
+
+
+class Finance(Base):
+ __tablename__ = 'finances'
+ id = Column(Integer, primary_key=True)
+ item_name = Column(String)
+ category = Column(String)
+ cost = Column(Float)
+ date = Column(String)
+
+
+# Determine the application data directory based on the operating system using pathlib
+if platform.system() == 'Windows':
+ app_data_location = Path(os.getenv('APPDATA')) / 'FinanceManager'
+elif platform.system() == 'Darwin': # macOS
+ app_data_location = Path.home() / 'Library' / 'Application Support' / 'FinanceManager'
+else: # Linux and other Unix-like systems
+ app_data_location = Path.home() / '.local' / 'share' / 'FinanceManager'
+
+db_path = app_data_location / 'finances.db'
+
+DATABASE_URL = f'sqlite:///{db_path}'
+engine = create_engine(DATABASE_URL)
+Session = sessionmaker(bind=engine)
+
+# Default data to be added to the database
+default_data = [
+ {"item_name": "Mobile Prepaid", "category": "Electronics", "cost": 20.00, "date": "15-02-2024"},
+ {"item_name": "Groceries-Feb-Week1", "category": "Groceries", "cost": 60.75,
+ "date": "16-01-2024"},
+ {"item_name": "Bus Ticket", "category": "Transport", "cost": 5.50, "date": "17-01-2024"},
+ {"item_name": "Book", "category": "Education", "cost": 25.00, "date": "18-01-2024"},
+]
+
+
+def initialize_database():
+ if db_path.exists():
+ print(f"Database '{db_path}' already exists.")
+ return
+
+ app_data_location.mkdir(parents=True, exist_ok=True)
+ Base.metadata.create_all(engine)
+ print(f"Database '{db_path}' created successfully.")
+ session = Session()
+
+ for data in default_data:
+ finance = Finance(**data)
+ session.add(finance)
+
+ session.commit()
+ print("Default data has been added to the database.")
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import uvicorn
+from database import initialize_database
+
+
+def main():
+ # Initialize the database
+ initialize_database()
+ # Start the FastAPI endpoint
+ uvicorn.run("rest_api:app", host="127.0.0.1", port=8000, reload=True)
+
+
+if __name__ == "__main__":
+ main()
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import logging
+from fastapi import FastAPI, Depends, HTTPException
+from pydantic import BaseModel
+from typing import Dict, Any
+from sqlalchemy import orm
+from database import Session, Finance
+
+app = FastAPI()
+
+
+class FinanceCreate(BaseModel):
+ item_name: str
+ category: str
+ cost: float
+ date: str
+
+
+class FinanceRead(FinanceCreate):
+ class Config:
+ from_attributes = True
+
+
+def get_db():
+ db = Session()
+ try:
+ yield db
+ finally:
+ db.close()
+
+
+@app.post("/finances/", response_model=FinanceRead)
+def create_finance(finance: FinanceCreate, db: orm.Session = Depends(get_db)):
+ print(f"Adding finance item: {finance}")
+ db_finance = Finance(**finance.model_dump())
+ db.add(db_finance)
+ db.commit()
+ db.refresh(db_finance)
+ return db_finance
+
+
+@app.get("/finances/", response_model=Dict[str, Any])
+def read_finances(skip: int = 0, limit: int = 10, db: orm.Session = Depends(get_db)):
+ try:
+ total = db.query(Finance).count()
+ finances = db.query(Finance).offset(skip).limit(limit).all()
+ response = {
+ "total": total,
+ # Convert the list of Finance objects to a list of FinanceRead objects
+ "items": [FinanceRead.from_orm(finance) for finance in finances]
+ }
+ logging.info(f"Response: {response}")
+ return response
+ except Exception as e:
+ logging.error(f"Error occurred: {e}")
+ raise HTTPException(status_code=500, detail="Internal Server Error")
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Dialog {
+ id: dialog
+
+ signal finished(string itemName, string category, real cost, string date)
+
+ contentItem: ColumnLayout {
+ id: form
+ spacing: 10
+ property alias itemName: itemName
+ property alias category: category
+ property alias cost: cost
+ property alias date: date
+
+ GridLayout {
+ columns: 2
+ columnSpacing: 20
+ rowSpacing: 10
+ Layout.fillWidth: true
+
+ Label {
+ text: qsTr("Item Name:")
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ TextField {
+ id: itemName
+ focus: true
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ Label {
+ text: qsTr("Category:")
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ TextField {
+ id: category
+ focus: true
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ Label {
+ text: qsTr("Cost:")
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ TextField {
+ id: cost
+ focus: true
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ placeholderText: qsTr("€")
+ inputMethodHints: Qt.ImhFormattedNumbersOnly
+ }
+
+ Label {
+ text: qsTr("Date:")
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+
+ TextField {
+ id: date
+ focus: true
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ placeholderText: qsTr("dd-mm-yyyy")
+ validator: RegularExpressionValidator { regularExpression: /^[0-3]?\d-[01]?\d-\d{4}$/ }
+ // code to add the - automatically
+ onTextChanged: {
+ if (date.text.length === 2 || date.text.length === 5) {
+ date.text += "-"
+ }
+ }
+ Component.onCompleted: {
+ var today = new Date();
+ var day = String(today.getDate()).padStart(2, '0');
+ var month = String(today.getMonth() + 1).padStart(2, '0'); // Months are zero-based
+ var year = today.getFullYear();
+ date.placeholderText = day + "-" + month + "-" + year;
+ }
+ }
+ }
+ }
+
+ function createEntry() {
+ form.itemName.clear()
+ form.category.clear()
+ form.cost.clear()
+ form.date.clear()
+ dialog.title = qsTr("Add Finance Item")
+ dialog.open()
+ }
+
+ x: parent.width / 2 - width / 2
+ y: parent.height / 2 - height / 2
+
+ focus: true
+ modal: true
+ title: qsTr("Add Finance Item")
+ standardButtons: Dialog.Ok | Dialog.Cancel
+
+ Component.onCompleted: {
+ dialog.visible = false
+ Qt.inputMethod.visibleChanged.connect(adjustDialogPosition)
+ }
+
+ function adjustDialogPosition() {
+ if (Qt.inputMethod.visible) {
+ // If the keyboard is visible, move the dialog up
+ dialog.y = parent.height / 4 - height / 2
+ } else {
+ // If the keyboard is not visible, center the dialog
+ dialog.y = parent.height / 2 - height / 2
+ }
+ }
+
+ onAccepted: {
+ finished(form.itemName.text, form.category.text, parseFloat(form.cost.text), form.date.text)
+ }
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+ItemDelegate {
+ id: delegate
+ checkable: true
+ width: parent.width
+ height: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.15 :
+ Math.min(window.width, window.height) * 0.1
+
+ contentItem:
+ RowLayout {
+ Label {
+ id: dateLabel
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.03 :
+ Math.min(window.width, window.height) * 0.02
+ text: date
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ Layout.preferredWidth: 1
+ color: Material.primaryTextColor
+ }
+
+ ColumnLayout {
+ spacing: 5
+ Layout.fillWidth: true
+ Layout.preferredWidth: 1
+
+ Label {
+ text: item_name
+ color: "#5c8540"
+ font.bold: true
+ elide: Text.ElideRight
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.04 :
+ Math.min(window.width, window.height) * 0.02
+ Layout.fillWidth: true
+ }
+
+ Label {
+ text: category
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.03 :
+ Math.min(window.width, window.height) * 0.02
+ }
+ }
+
+ Item {
+ Layout.fillWidth: true // This item will take up the remaining space
+ }
+
+ ColumnLayout {
+ spacing: 5
+ Layout.fillWidth: true
+ Layout.preferredWidth: 1
+
+ Label {
+ text: "you spent:"
+ color: "#5c8540"
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.03 :
+ Math.min(window.width, window.height) * 0.02
+ }
+
+ Label {
+ text: cost + "€"
+ elide: Text.ElideRight
+ Layout.fillWidth: true
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.03 :
+ Math.min(window.width, window.height) * 0.02
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtGraphs
+import QtQuick.Controls.Material
+
+Item {
+ width: Screen.width
+ height: Screen.height
+
+ GraphsView {
+ id: chart
+ anchors.fill: parent
+ antialiasing: true
+
+ theme: GraphsTheme {
+ colorScheme: Qt.Dark
+ theme: GraphsTheme.Theme.QtGreenNeon
+ }
+
+ PieSeries {
+ id: pieSeries
+ }
+ }
+
+ Text {
+ id: chartTitle
+ text: "Total Expenses Breakdown by Category"
+ color: "#5c8540"
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.04 :
+ Math.min(window.width, window.height) * 0.03
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: parent.top
+ anchors.topMargin: 20
+ }
+
+ function updateChart(data) {
+ pieSeries.clear()
+ for (var category in data) {
+ var slice = pieSeries.append(category, data[category])
+ slice.label = category + ": " + data[category] + "€"
+ slice.labelVisible = true
+ }
+ }
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Material
+
+ListView {
+ id: listView
+ anchors.fill: parent
+ height: parent.height
+ property var financeModel
+
+ delegate: FinanceDelegate {
+ id: delegate
+ width: listView.width
+ }
+
+ model: financeModel
+
+ section.property: "month" // Group items by the "month" property
+ section.criteria: ViewSection.FullString
+ section.delegate: Component {
+ id: sectionHeading
+ Rectangle {
+ width: listView.width
+ height: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.05 :
+ Math.min(window.width, window.height) * 0.03
+ color: "#5c8540"
+
+ required property string section
+
+ Text {
+ text: parent.section
+ font.bold: true
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.03 :
+ Math.min(window.width, window.height) * 0.02
+ color: Material.primaryTextColor
+ }
+ }
+ }
+
+ ScrollBar.vertical: ScrollBar { }
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtQuick.Controls.Material
+import Finance
+
+ApplicationWindow {
+ id: window
+ Material.theme: Material.Dark
+ Material.accent: Material.Gray
+ width: Screen.width * 0.3
+ height: Screen.height * 0.5
+ visible: true
+ title: qsTr("Finance Manager")
+
+ // Add a toolbar for the application, only visible on mobile
+ header: ToolBar {
+ Material.primary: "#5c8540"
+ visible: Qt.platform.os == "android"
+ RowLayout {
+ anchors.fill: parent
+ Label {
+ text: qsTr("Finance Manager")
+ font.pixelSize: 20
+ Layout.alignment: Qt.AlignCenter
+ }
+ }
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ TabBar {
+ id: tabBar
+ Layout.fillWidth: true
+
+ TabButton {
+ text: qsTr("Expenses")
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.04 :
+ Math.min(window.width, window.height) * 0.02
+ onClicked: stackView.currentIndex = 0
+ }
+
+ TabButton {
+ text: qsTr("Charts")
+ font.pixelSize: Qt.platform.os == "android" ?
+ Math.min(window.width, window.height) * 0.04 :
+ Math.min(window.width, window.height) * 0.02
+ onClicked: stackView.currentIndex = 1
+ }
+ }
+
+ StackLayout {
+ id: stackView
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ Item {
+ id: expensesView
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ FinanceView {
+ id: financeView
+ anchors.fill: parent
+ financeModel: finance_model
+ }
+ }
+
+ Item {
+ id: chartsView
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ FinancePieChart {
+ id: financePieChart
+ anchors.fill: parent
+ Component.onCompleted: {
+ var categoryData = finance_model.getCategoryData()
+ updateChart(categoryData)
+ }
+ }
+ }
+ }
+ }
+
+ // Model to store the finance data. Created from Python.
+ FinanceModel {
+ id: finance_model
+ }
+
+ // Add a dialog to add new entries
+ AddDialog {
+ id: addDialog
+ onFinished: function(item_name, category, cost, date) {
+ finance_model.append(item_name, category, cost, date)
+ var categoryData = finance_model.getCategoryData()
+ financePieChart.updateChart(categoryData)
+ }
+ }
+
+ // Add a button to open the dialog
+ ToolButton {
+ id: roundButton
+ text: qsTr("+")
+ highlighted: true
+ Material.elevation: 6
+ width: Qt.platform.os === "android" ?
+ Math.min(parent.width * 0.2, Screen.width * 0.15) :
+ Math.min(parent.width * 0.060, Screen.width * 0.05)
+ height: width // Keep the button circular
+ anchors.margins: 10
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ background: Rectangle {
+ color: "#5c8540"
+ radius: roundButton.width / 2
+ }
+ font.pixelSize: width * 0.4
+ onClicked: {
+ addDialog.createEntry()
+ }
+ }
+}
--- /dev/null
+module Finance
+Main 1.0 Main.qml
+FinanceView 1.0 FinanceView.qml
+FinancePieChart 1.0 FinancePieChart.qml
+FinanceDelegate 1.0 FinanceDelegate.qml
+AddDialog 1.0 AddDialog.qml
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import requests
+from datetime import datetime
+from dataclasses import dataclass
+from enum import IntEnum
+from collections import defaultdict
+
+from PySide6.QtCore import (QAbstractListModel, QEnum, Qt, QModelIndex, Slot,
+ QByteArray)
+from PySide6.QtQml import QmlElement
+
+QML_IMPORT_NAME = "Finance"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class FinanceModel(QAbstractListModel):
+
+ @QEnum
+ class FinanceRole(IntEnum):
+ ItemNameRole = Qt.ItemDataRole.DisplayRole
+ CategoryRole = Qt.ItemDataRole.UserRole
+ CostRole = Qt.ItemDataRole.UserRole + 1
+ DateRole = Qt.ItemDataRole.UserRole + 2
+ MonthRole = Qt.ItemDataRole.UserRole + 3
+
+ @dataclass
+ class Finance:
+ item_name: str
+ category: str
+ cost: float
+ date: str
+
+ @property
+ def month(self):
+ return datetime.strptime(self.date, "%d-%m-%Y").strftime("%B %Y")
+
+ def __init__(self, parent=None) -> None:
+ super().__init__(parent)
+ self.m_finances = []
+ self.fetchAllData()
+
+ def fetchAllData(self):
+ response = requests.get("http://127.0.0.1:8000/finances/")
+ try:
+ data = response.json()
+ except requests.exceptions.JSONDecodeError:
+ print("Failed to decode JSON response")
+ return
+ self.beginInsertRows(QModelIndex(), 0, len(data["items"]) - 1)
+ self.m_finances.extend([self.Finance(**item) for item in data["items"]])
+ self.endInsertRows()
+
+ def rowCount(self, parent=QModelIndex()):
+ return len(self.m_finances)
+
+ def data(self, index: QModelIndex, role: int):
+ if not index.isValid() or index.row() >= self.rowCount():
+ return None
+ row = index.row()
+ if row < self.rowCount():
+ finance = self.m_finances[row]
+ if role == FinanceModel.FinanceRole.ItemNameRole:
+ return finance.item_name
+ if role == FinanceModel.FinanceRole.CategoryRole:
+ return finance.category
+ if role == FinanceModel.FinanceRole.CostRole:
+ return finance.cost
+ if role == FinanceModel.FinanceRole.DateRole:
+ return finance.date
+ if role == FinanceModel.FinanceRole.MonthRole:
+ return finance.month
+ return None
+
+ def roleNames(self):
+ roles = super().roleNames()
+ roles[FinanceModel.FinanceRole.ItemNameRole] = QByteArray(b"item_name")
+ roles[FinanceModel.FinanceRole.CategoryRole] = QByteArray(b"category")
+ roles[FinanceModel.FinanceRole.CostRole] = QByteArray(b"cost")
+ roles[FinanceModel.FinanceRole.DateRole] = QByteArray(b"date")
+ roles[FinanceModel.FinanceRole.MonthRole] = QByteArray(b"month")
+ return roles
+
+ @Slot(int, result='QVariantMap')
+ def get(self, row: int):
+ finance = self.m_finances[row]
+ return {"item_name": finance.item_name, "category": finance.category,
+ "cost": finance.cost, "date": finance.date}
+
+ @Slot(str, str, float, str)
+ def append(self, item_name: str, category: str, cost: float, date: str):
+ finance = {"item_name": item_name, "category": category, "cost": cost, "date": date}
+ response = requests.post("http://127.0.0.1:8000/finances/", json=finance)
+ if response.status_code == 200:
+ finance = response.json()
+ self.beginInsertRows(QModelIndex(), 0, 0)
+ self.m_finances.insert(0, self.Finance(**finance))
+ self.endInsertRows()
+ else:
+ print("Failed to add finance item")
+
+ @Slot(result=dict)
+ def getCategoryData(self):
+ category_data = defaultdict(float)
+ for finance in self.m_finances:
+ category_data[finance.category] += finance.cost
+ return dict(category_data)
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from pathlib import Path
+
+from PySide6.QtWidgets import QApplication
+from PySide6.QtQml import QQmlApplicationEngine
+
+from financemodel import FinanceModel # noqa: F401
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ QApplication.setOrganizationName("QtProject")
+ QApplication.setApplicationName("Finance Manager")
+ engine = QQmlApplicationEngine()
+
+ engine.addImportPath(Path(__file__).parent)
+ engine.loadFromModule("Finance", "Main")
+
+ if not engine.rootObjects():
+ sys.exit(-1)
+
+ exit_code = app.exec()
+ del engine
+ sys.exit(exit_code)
--- /dev/null
+# Finance Manager Example - Part 3
+
+This example represents the final part of the tutorial series on creating a simple Finance Manager
+that allows users to manage their expenses and visualize them using a pie chart, using PySide6,
+[SQLAlchemy], [FastAPI], and [Pydantic].
+
+For more details, see the [Finance Manager Tutorial - Part 3](tutorial_financemanager_partthree).
+
+[SQLalchemy]: https://www.sqlalchemy.org/
+[FastApi]: https://fastapi.tiangolo.com/
+[PyDantic]: https://pydantic-docs.helpmanual.io/
--- /dev/null
+{
+ "files": [
+ "Backend/database.py",
+ "Backend/main.py",
+ "Backend/rest_api.py",
+ "Frontend/Finance/AddDialog.qml",
+ "Frontend/Finance/FinanceDelegate.qml",
+ "Frontend/Finance/FinancePieChart.qml",
+ "Frontend/Finance/FinanceView.qml",
+ "Frontend/Finance/Main.qml",
+ "Frontend/Finance/qmldir",
+ "Frontend/financemodel.py",
+ "Frontend/main.py",
+ "requirements.txt"
+ ]
+}
--- /dev/null
+sqlalchemy
+uvicorn
+fastapi
--- /dev/null
+UILoader Example
+================
+
+This example demonstrates how to dynamically load and display a user interface
+designed with Qt Designer using PySide6's `QUiLoader`. It shows how to load a
+`.ui` file at runtime, allowing for flexible and dynamic UI design and
+modification without recompiling the application.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""QUiLoader example, showing how to dynamically load a Qt Designer form
from a UI file."""
app = QApplication(sys.argv)
ui_file = QFile(ui_file_name)
- if not ui_file.open(QIODevice.ReadOnly):
+ if not ui_file.open(QIODevice.OpenModeFlag.ReadOnly):
reason = ui_file.errorString()
print(f"Cannot open {ui_file_name}: {reason}")
sys.exit(-1)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sysconfig
from enum import Enum
def get_python_include_path():
- return sysconfig.get_path('include')
+ if sys.platform == 'win32':
+ return sysconfig.get_path('include')
+ else:
+ return sysconfig.get_path('include', scheme="posix_prefix")
def python_link_flags_qmake():
# Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Signal, Slot
# Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Signal, Slot
-# Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
+# Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com,
+# author Milian Wolff <milian.wolff@kdab.com>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
# setup the QWebSocketServer
server = QWebSocketServer("QWebChannel Standalone Example Server",
- QWebSocketServer.NonSecureMode)
- if not server.listen(QHostAddress.LocalHost, 12345):
+ QWebSocketServer.SslMode.NonSecureMode)
+ if not server.listen(QHostAddress.SpecialAddress.LocalHost, 12345):
print("Failed to open web socket server.")
sys.exit(-1)
################################################################################
## Form generated from reading UI file 'dialog.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
# Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Signal, Slot
# Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWebChannel import QWebChannelAbstractTransport
from PySide6.QtCore import QByteArray, QJsonDocument, Slot
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 WebEngine QtQuick 2 Example"""
QMetaObject.invokeMethod(engine.rootObjects()[0], "load", Q_ARG("QVariant", url))
- app.exec()
+ exit_code = app.exec()
+ del engine
+ sys.exit(exit_code)
--- /dev/null
+WebEngine Markdown Editor Example
+=================================
+
+Demonstrates how to integrate a web engine in a hybrid desktop application.
+It corresponds to the Qt example `WebEngine Markdown Editor Example
+<https://doc.qt.io/qt-5/qtwebengine-webenginewidgets-markdowneditor-example.html>`_.
+
+.. image:: markdowneditor.png
+ :align: center
+ :alt: markdowneditor screenshot
+ :width: 400
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QObject, Property, Signal
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 Markdown Editor Example"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QDir, QFile, QIODevice, QUrl, Qt, Slot
self.m_content = Document()
self._ui = Ui_MainWindow()
self._ui.setupUi(self)
- font = QFontDatabase.systemFont(QFontDatabase.FixedFont)
+ font = QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont)
self._ui.editor.setFont(font)
- self._ui.preview.setContextMenuPolicy(Qt.NoContextMenu)
+ self._ui.preview.setContextMenuPolicy(Qt.ContextMenuPolicy.NoContextMenu)
self._page = PreviewPage(self)
self._ui.preview.setPage(self._page)
self._ui.editor.document().modificationChanged.connect(self._ui.actionSave.setEnabled)
defaultTextFile = QFile(":/default.md")
- defaultTextFile.open(QIODevice.ReadOnly)
+ defaultTextFile.open(QIODevice.OpenModeFlag.ReadOnly)
data = defaultTextFile.readAll()
self._ui.editor.setPlainText(data.data().decode('utf8'))
def openFile(self, path):
f = QFile(path)
name = QDir.toNativeSeparators(path)
- if not f.open(QIODevice.ReadOnly):
+ if not f.open(QIODevice.OpenModeFlag.ReadOnly):
error = f.errorString()
QMessageBox.warning(self, self.windowTitle(),
f"Could not open file {name}: {error}")
if self.isModified():
m = "You have unsaved changes. Do you want to create a new document anyway?"
button = QMessageBox.question(self, self.windowTitle(), m)
- if button != QMessageBox.Yes:
+ if button != QMessageBox.StandardButton.Yes:
return
self.m_file_path = ''
if self.isModified():
m = "You have unsaved changes. Do you want to open a new document anyway?"
button = QMessageBox.question(self, self.windowTitle(), m)
- if button != QMessageBox.Yes:
+ if button != QMessageBox.StandardButton.Yes:
return
dialog = QFileDialog(self)
dialog.setWindowTitle("Open MarkDown File")
dialog.setMimeTypeFilters(["text/markdown"])
- dialog.setAcceptMode(QFileDialog.AcceptOpen)
- if dialog.exec() == QDialog.Accepted:
+ dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptOpen)
+ if dialog.exec() == QDialog.DialogCode.Accepted:
self.openFile(dialog.selectedFiles()[0])
@Slot()
f = QFile(self.m_file_path)
name = QDir.toNativeSeparators(self.m_file_path)
- if not f.open(QIODevice.WriteOnly | QIODevice.Text):
+ if not f.open(QIODevice.OpenModeFlag.WriteOnly | QIODevice.OpenModeFlag.Text):
error = f.errorString()
QMessageBox.warning(self, self.windowTitle(),
f"Could not write to file {name}: {error}")
dialog = QFileDialog(self)
dialog.setWindowTitle("Save MarkDown File")
dialog.setMimeTypeFilters(["text/markdown"])
- dialog.setAcceptMode(QFileDialog.AcceptSave)
+ dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
dialog.setDefaultSuffix("md")
- if dialog.exec() != QDialog.Accepted:
+ if dialog.exec() != QDialog.DialogCode.Accepted:
return
path = dialog.selectedFiles()[0]
self.m_file_path = path
if self.isModified():
m = "You have unsaved changes. Do you want to exit anyway?"
button = QMessageBox.question(self, self.windowTitle(), m)
- if button != QMessageBox.Yes:
+ if button != QMessageBox.StandardButton.Yes:
event.ignore()
else:
event.accept()
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<widget class="QPlainTextEdit" name="editor"/>
<widget class="QWebEngineView" name="preview" native="true"/>
</property>
</action>
</widget>
+ <customwidgets>
+ <customwidget>
+ <class>QWebEngineView</class>
+ <extends>QWidget</extends>
+ <header>qwebengineview.h</header>
+ </customwidget>
+ </customwidgets>
<resources/>
<connections/>
</ui>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtGui import QDesktopServices
from PySide6.QtWebEngineCore import QWebEnginePage
</script>
</body>
</html>
-
-
-
################################################################################
## Form generated from reading UI file 'mainwindow.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.splitter = QSplitter(self.centralwidget)
self.splitter.setObjectName(u"splitter")
- self.splitter.setOrientation(Qt.Horizontal)
+ self.splitter.setOrientation(Qt.Orientation.Horizontal)
self.editor = QPlainTextEdit(self.splitter)
self.editor.setObjectName(u"editor")
self.splitter.addWidget(self.editor)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 WebEngineWidgets Notifications Example"""
view.setPage(WebEnginePage(view))
def set_feature_permission(origin: QUrl, feature: QWebEnginePage.Feature):
- if feature != QWebEnginePage.Notifications:
+ if feature != QWebEnginePage.Feature.Notifications:
return
- view.page().setFeaturePermission(origin, feature, QWebEnginePage.PermissionGrantedByUser)
+ view.page().setFeaturePermission(origin, feature,
+ QWebEnginePage.PermissionPolicy.PermissionGrantedByUser)
view.page().featurePermissionRequested.connect(set_feature_permission)
profile = view.page().profile()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Qt, QTimer, QPoint, Slot
from PySide6.QtWidgets import (QWidget, QHBoxLayout, QLabel, QVBoxLayout, QSpacerItem, QSizePolicy,
super().__init__(parent)
self.notification = None
self.m_icon, self.m_title, self.m_message = QLabel(), QLabel(), QLabel()
- self.setWindowFlags(Qt.ToolTip)
+ self.setWindowFlags(Qt.WindowType.ToolTip)
rootLayout = QHBoxLayout(self)
rootLayout.addWidget(self.m_icon)
bodyLayout.addLayout(titleLayout)
titleLayout.addWidget(self.m_title)
- titleLayout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding))
+ titleLayout.addItem(QSpacerItem(0, 0, QSizePolicy.Policy.Expanding))
close = QPushButton("Close")
titleLayout.addWidget(close)
def mouseReleaseEvent(self, event: QMouseEvent) -> None:
QWidget.mouseReleaseEvent(event)
- if self.notification and event.button() == Qt.LeftButton:
+ if self.notification and event.button() == Qt.MouseButton.LeftButton:
self.notification.click()
self.onClosed()
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWebEngineCore import (qWebEngineChromiumVersion,
QWebEngineProfile, QWebEngineSettings)
# Quit application if the download manager window is the only
# remaining window
- self._download_manager_widget.setAttribute(Qt.WA_QuitOnClose, False)
+ self._download_manager_widget.setAttribute(Qt.WidgetAttribute.WA_QuitOnClose, False)
dp = QWebEngineProfile.defaultProfile()
dp.downloadRequested.connect(self._download_manager_widget.download_requested)
name = "simplebrowser." + qWebEngineChromiumVersion()
self._profile = QWebEngineProfile(name)
s = self._profile.settings()
- s.setAttribute(QWebEngineSettings.PluginsEnabled, True)
- s.setAttribute(QWebEngineSettings.DnsPrefetchEnabled, True)
- s.setAttribute(QWebEngineSettings.LocalContentCanAccessRemoteUrls, True)
- s.setAttribute(QWebEngineSettings.LocalContentCanAccessFileUrls, False)
+ s.setAttribute(QWebEngineSettings.WebAttribute.PluginsEnabled, True)
+ s.setAttribute(QWebEngineSettings.WebAttribute.DnsPrefetchEnabled, True)
+ s.setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True)
+ s.setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessFileUrls, False)
self._profile.downloadRequested.connect(
self._download_manager_widget.download_requested)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
result = keys.copy()
# Chromium already handles navigate on backspace when appropriate.
for i, key in enumerate(result):
- if (key[0].key() & Qt.Key_unknown) == Qt.Key_Backspace:
+ if (key[0].key() & Qt.Key.Key_unknown) == Qt.Key.Key_Backspace:
del result[i]
break
return result
self._reload_icon = QIcon.fromTheme(QIcon.ThemeIcon.ViewRefresh,
QIcon(":view-refresh.png"))
- self.setAttribute(Qt.WA_DeleteOnClose, True)
- self.setFocusPolicy(Qt.ClickFocus)
+ self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
+ self.setFocusPolicy(Qt.FocusPolicy.ClickFocus)
if not forDevTools:
self._progress_bar = QProgressBar(self)
focus_url_line_edit_action = QAction(self)
self.addAction(focus_url_line_edit_action)
- focus_url_line_edit_action.setShortcut(QKeySequence(Qt.CTRL | Qt.Key_L))
+ focus_url_line_edit_action.setShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_L))
focus_url_line_edit_action.triggered.connect(self._focus_url_lineEdit)
self.handle_web_view_title_changed("")
def create_file_menu(self, tabWidget):
file_menu = QMenu("File")
- file_menu.addAction("&New Window", QKeySequence.New,
+ file_menu.addAction("&New Window", QKeySequence.StandardKey.New,
self.handle_new_window_triggered)
file_menu.addAction("New &Incognito Window",
self.handle_new_incognito_window_triggered)
new_tab_action = QAction("New Tab", self)
- new_tab_action.setShortcuts(QKeySequence.AddTab)
+ new_tab_action.setShortcuts(QKeySequence.StandardKey.AddTab)
new_tab_action.triggered.connect(self._new_tab)
file_menu.addAction(new_tab_action)
- file_menu.addAction("&Open File...", QKeySequence.Open,
+ file_menu.addAction("&Open File...", QKeySequence.StandardKey.Open,
self.handle_file_open_triggered)
file_menu.addSeparator()
close_tab_action = QAction("Close Tab", self)
- close_tab_action.setShortcuts(QKeySequence.Close)
+ close_tab_action.setShortcuts(QKeySequence.StandardKey.Close)
close_tab_action.triggered.connect(self._close_current_tab)
file_menu.addAction(close_tab_action)
self._close_action = QAction("Quit", self)
- self._close_action.setShortcut(QKeySequence(Qt.CTRL | Qt.Key_Q))
+ self._close_action.setShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Q))
self._close_action.triggered.connect(self.close)
file_menu.addAction(self._close_action)
def create_edit_menu(self):
edit_menu = QMenu("Edit")
find_action = edit_menu.addAction("Find")
- find_action.setShortcuts(QKeySequence.Find)
+ find_action.setShortcuts(QKeySequence.StandardKey.Find)
find_action.triggered.connect(self.handle_find_action_triggered)
find_next_action = edit_menu.addAction("Find Next")
- find_next_action.setShortcut(QKeySequence.FindNext)
+ find_next_action.setShortcut(QKeySequence.StandardKey.FindNext)
find_next_action.triggered.connect(self._find_next)
find_previous_action = edit_menu.addAction("Find Previous")
- find_previous_action.setShortcut(QKeySequence.FindPrevious)
+ find_previous_action.setShortcut(QKeySequence.StandardKey.FindPrevious)
find_previous_action.triggered.connect(self._find_previous)
return edit_menu
view_menu = QMenu("View")
self._stop_action = view_menu.addAction("Stop")
shortcuts = []
- shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_Period))
- shortcuts.append(QKeySequence(Qt.Key_Escape))
+ shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Period))
+ shortcuts.append(QKeySequence(Qt.Key.Key_Escape))
self._stop_action.setShortcuts(shortcuts)
self._stop_action.triggered.connect(self._stop)
self._reload_action = view_menu.addAction("Reload Page")
- self._reload_action.setShortcuts(QKeySequence.Refresh)
+ self._reload_action.setShortcuts(QKeySequence.StandardKey.Refresh)
self._reload_action.triggered.connect(self._reload)
zoom_in = view_menu.addAction("Zoom In")
- zoom_in.setShortcut(QKeySequence(Qt.CTRL | Qt.Key_Plus))
+ zoom_in.setShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Plus))
zoom_in.triggered.connect(self._zoom_in)
zoom_out = view_menu.addAction("Zoom Out")
- zoom_out.setShortcut(QKeySequence(Qt.CTRL | Qt.Key_Minus))
+ zoom_out.setShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Minus))
zoom_out.triggered.connect(self._zoom_out)
reset_zoom = view_menu.addAction("Reset Zoom")
- reset_zoom.setShortcut(QKeySequence(Qt.CTRL | Qt.Key_0))
+ reset_zoom.setShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_0))
reset_zoom.triggered.connect(self._reset_zoom)
view_menu.addSeparator()
menu = QMenu("Window")
self._next_tab_action = QAction("Show Next Tab", self)
shortcuts = []
- shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_BraceRight))
- shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_PageDown))
- shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_BracketRight))
- shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_Less))
+ shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_BraceRight))
+ shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_PageDown))
+ shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_BracketRight))
+ shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Less))
self._next_tab_action.setShortcuts(shortcuts)
self._next_tab_action.triggered.connect(tabWidget.next_tab)
self._previous_tab_action = QAction("Show Previous Tab", self)
shortcuts.clear()
- shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_BraceLeft))
- shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_PageUp))
- shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_BracketLeft))
- shortcuts.append(QKeySequence(Qt.CTRL | Qt.Key_Greater))
+ shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_BraceLeft))
+ shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_PageUp))
+ shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_BracketLeft))
+ shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Greater))
self._previous_tab_action.setShortcuts(shortcuts)
self._previous_tab_action.triggered.connect(tabWidget.previous_tab)
self._inspector_action = QAction("Open inspector in window", self)
shortcuts.clear()
- shortcuts.append(QKeySequence(Qt.CTRL | Qt.SHIFT | Qt.Key_I))
+ shortcuts.append(QKeySequence(Qt.Modifier.CTRL | Qt.Modifier.SHIFT | Qt.Key.Key_I))
self._inspector_action.setShortcuts(shortcuts)
self._inspector_action.triggered.connect(self._emit_dev_tools_requested)
self._window_menu = menu
@Slot()
def _back(self):
- self._tab_widget.trigger_web_page_action(QWebEnginePage.Back)
+ self._tab_widget.trigger_web_page_action(QWebEnginePage.WebAction.Back)
@Slot()
def _forward(self):
- self._tab_widget.trigger_web_page_action(QWebEnginePage.Forward)
+ self._tab_widget.trigger_web_page_action(QWebEnginePage.WebAction.Forward)
@Slot()
def _stop_reload(self):
navigation_bar.toggleViewAction().setEnabled(False)
self._history_back_action = QAction(self)
- back_shortcuts = remove_backspace(QKeySequence.keyBindings(QKeySequence.Back))
+ back_shortcuts = remove_backspace(QKeySequence.keyBindings(QKeySequence.StandardKey.Back))
# For some reason Qt doesn't bind the dedicated Back key to Back.
- back_shortcuts.append(QKeySequence(Qt.Key_Back))
+ back_shortcuts.append(QKeySequence(Qt.Key.Key_Back))
self._history_back_action.setShortcuts(back_shortcuts)
self._history_back_action.setIconVisibleInMenu(False)
back_icon = QIcon.fromTheme(QIcon.ThemeIcon.GoPrevious,
navigation_bar.addAction(self._history_back_action)
self._history_forward_action = QAction(self)
- fwd_shortcuts = remove_backspace(QKeySequence.keyBindings(QKeySequence.Forward))
- fwd_shortcuts.append(QKeySequence(Qt.Key_Forward))
+ fwd_shortcuts = remove_backspace(QKeySequence.keyBindings(QKeySequence.StandardKey.Forward))
+ fwd_shortcuts.append(QKeySequence(Qt.Key.Key_Forward))
self._history_forward_action.setShortcuts(fwd_shortcuts)
self._history_forward_action.setIconVisibleInMenu(False)
next_icon = QIcon.fromTheme(QIcon.ThemeIcon.GoNext,
self._url_line_edit = QLineEdit(self)
self._fav_action = QAction(self)
- self._url_line_edit.addAction(self._fav_action, QLineEdit.LeadingPosition)
+ self._url_line_edit.addAction(self._fav_action, QLineEdit.ActionPosition.LeadingPosition)
self._url_line_edit.setClearButtonEnabled(True)
navigation_bar.addWidget(self._url_line_edit)
return navigation_bar
def handle_web_action_enabled_changed(self, action, enabled):
- if action == QWebEnginePage.Back:
+ if action == QWebEnginePage.WebAction.Back:
self._history_back_action.setEnabled(enabled)
- elif action == QWebEnginePage.Forward:
+ elif action == QWebEnginePage.WebAction.Forward:
self._history_forward_action.setEnabled(enabled)
- elif action == QWebEnginePage.Reload:
+ elif action == QWebEnginePage.WebAction.Reload:
self._reload_action.setEnabled(enabled)
- elif action == QWebEnginePage.Stop:
+ elif action == QWebEnginePage.WebAction.Stop:
self._stop_action.setEnabled(enabled)
else:
print("Unhandled webActionChanged signal", file=sys.stderr)
if not self.current_tab():
return
search, ok = QInputDialog.getText(self, "Find", "Find:",
- QLineEdit.Normal, self._last_search)
+ QLineEdit.EchoMode.Normal, self._last_search)
if ok and search:
self._last_search = search
self.current_tab().findText(self._last_search)
def handle_web_view_load_progress(self, progress):
if 0 < progress and progress < 100:
- self._stop_reload_action.setData(QWebEnginePage.Stop)
+ self._stop_reload_action.setData(QWebEnginePage.WebAction.Stop)
self._stop_reload_action.setIcon(self._stop_icon)
self._stop_reload_action.setToolTip("Stop loading the current page")
self._progress_bar.setValue(progress)
else:
- self._stop_reload_action.setData(QWebEnginePage.Reload)
+ self._stop_reload_action.setData(QWebEnginePage.WebAction.Reload)
self._stop_reload_action.setIcon(self._reload_icon)
self._stop_reload_action.setToolTip("Reload the current page")
self._progress_bar.setValue(0)
def handle_dev_tools_requested(self, source):
page = self._browser.create_dev_tools_window().current_tab().page()
source.setDevToolsPage(page)
- source.triggerAction(QWebEnginePage.InspectElement)
+ source.triggerAction(QWebEnginePage.WebAction.InspectElement)
def handle_find_text_finished(self, result):
sb = self.statusBar()
if result.numberOfMatches() == 0:
- sb.showMessage(f'"{self._lastSearch}" not found.')
+ sb.showMessage(f'"{self._last_search}" not found.')
else:
active = result.activeMatch()
number = result.numberOfMatches()
<rect>
<x>0</x>
<y>0</y>
- <width>370</width>
- <height>141</height>
+ <width>689</width>
+ <height>204</height>
</rect>
</property>
<property name="windowTitle">
<string>Icon</string>
</property>
<property name="alignment">
- <set>Qt::AlignCenter</set>
+ <set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
<string>Error</string>
</property>
<property name="alignment">
- <set>Qt::AlignCenter</set>
+ <set>Qt::AlignmentFlag::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
Do you wish to override the security check and continue ? </string>
</property>
<property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
- <enum>Qt::Vertical</enum>
+ <enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
- <set>QDialogButtonBox::No|QDialogButtonBox::Yes</set>
+ <set>QDialogButtonBox::StandardButton::No|QDialogButtonBox::StandardButton::Yes</set>
</property>
</widget>
</item>
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWebEngineCore import QWebEngineDownloadRequest
from PySide6.QtWidgets import QWidget, QFileDialog
</property>
<layout class="QVBoxLayout" name="m_topLevelLayout">
<property name="sizeConstraint">
- <enum>QLayout::SetNoConstraint</enum>
+ <enum>QLayout::SizeConstraint::SetNoConstraint</enum>
</property>
<property name="leftMargin">
<number>0</number>
}</string>
</property>
<property name="verticalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOn</enum>
+ <enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOn</enum>
</property>
<property name="horizontalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOff</enum>
+ <enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
</property>
<widget class="QWidget" name="m_items">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>382</width>
+ <height>208</height>
+ </rect>
+ </property>
<property name="styleSheet">
<string notr="true">#m_items {background: palette(mid)}</string>
</property>
<string>No downloads</string>
</property>
<property name="alignment">
- <set>Qt::AlignCenter</set>
+ <set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from ui_downloadwidget import Ui_DownloadWidget
<ui version="4.0">
<class>DownloadWidget</class>
<widget class="QFrame" name="DownloadWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>144</width>
+ <height>103</height>
+ </rect>
+ </property>
<property name="styleSheet">
<string notr="true">#DownloadWidget {
background: palette(button);
</property>
<layout class="QGridLayout" name="m_topLevelLayout">
<property name="sizeConstraint">
- <enum>QLayout::SetMinAndMaxSize</enum>
+ <enum>QLayout::SizeConstraint::SetMinAndMaxSize</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="m_dstName">
<item row="0" column="1">
<widget class="QPushButton" name="m_cancelButton">
<property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed"/>
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Qt WebEngineWidgets Simple Browser example from Qt v6.x"""
QLoggingCategory.setFilterRules("qt.webenginecontext.debug=true")
s = QWebEngineProfile.defaultProfile().settings()
- s.setAttribute(QWebEngineSettings.PluginsEnabled, True)
- s.setAttribute(QWebEngineSettings.DnsPrefetchEnabled, True)
+ s.setAttribute(QWebEngineSettings.WebAttribute.PluginsEnabled, True)
+ s.setAttribute(QWebEngineSettings.WebAttribute.DnsPrefetchEnabled, True)
browser = Browser()
window = browser.create_hidden_window()
<string>Icon</string>
</property>
<property name="alignment">
- <set>Qt::AlignCenter</set>
+ <set>Qt::AlignmentFlag::AlignCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="m_passwordLineEdit">
<property name="echoMode">
- <enum>QLineEdit::Password</enum>
+ <enum>QLineEdit::EchoMode::Password</enum>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from functools import partial
self._profile = profile
tab_bar = self.tabBar()
tab_bar.setTabsClosable(True)
- tab_bar.setSelectionBehaviorOnRemove(QTabBar.SelectPreviousTab)
+ tab_bar.setSelectionBehaviorOnRemove(QTabBar.SelectionBehavior.SelectPreviousTab)
tab_bar.setMovable(True)
- tab_bar.setContextMenuPolicy(Qt.CustomContextMenu)
+ tab_bar.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
tab_bar.customContextMenuRequested.connect(self.handle_context_menu_requested)
tab_bar.tabCloseRequested.connect(self.close_tab)
tab_bar.tabBarDoubleClicked.connect(self._tabbar_double_clicked)
self.setDocumentMode(True)
- self.setElideMode(Qt.ElideRight)
+ self.setElideMode(Qt.TextElideMode.ElideRight)
self.currentChanged.connect(self.handle_current_changed)
self.load_progress.emit(view.load_progress())
self.url_changed.emit(view.url())
self.fav_icon_changed.emit(view.fav_icon())
- e = view.is_web_action_enabled(QWebEnginePage.Back)
- self.web_action_enabled_changed.emit(QWebEnginePage.Back, e)
- e = view.is_web_action_enabled(QWebEnginePage.Forward)
- self.web_action_enabled_changed.emit(QWebEnginePage.Forward, e)
- e = view.is_web_action_enabled(QWebEnginePage.Stop)
- self.web_action_enabled_changed.emit(QWebEnginePage.Stop, e)
- e = view.is_web_action_enabled(QWebEnginePage.Reload)
- self.web_action_enabled_changed.emit(QWebEnginePage.Reload, e)
+ e = view.is_web_action_enabled(QWebEnginePage.WebAction.Back)
+ self.web_action_enabled_changed.emit(QWebEnginePage.WebAction.Back, e)
+ e = view.is_web_action_enabled(QWebEnginePage.WebAction.Forward)
+ self.web_action_enabled_changed.emit(QWebEnginePage.WebAction.Forward, e)
+ e = view.is_web_action_enabled(QWebEnginePage.WebAction.Stop)
+ self.web_action_enabled_changed.emit(QWebEnginePage.WebAction.Stop, e)
+ e = view.is_web_action_enabled(QWebEnginePage.WebAction.Reload)
+ self.web_action_enabled_changed.emit(QWebEnginePage.WebAction.Reload, e)
else:
self.title_changed.emit("")
self.load_progress.emit(0)
################################################################################
## Form generated from reading UI file 'certificateerrordialog.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
def setupUi(self, CertificateErrorDialog):
if not CertificateErrorDialog.objectName():
CertificateErrorDialog.setObjectName(u"CertificateErrorDialog")
- CertificateErrorDialog.resize(370, 141)
+ CertificateErrorDialog.resize(689, 204)
self.verticalLayout = QVBoxLayout(CertificateErrorDialog)
self.verticalLayout.setObjectName(u"verticalLayout")
self.verticalLayout.setContentsMargins(20, -1, 20, -1)
self.m_iconLabel = QLabel(CertificateErrorDialog)
self.m_iconLabel.setObjectName(u"m_iconLabel")
- self.m_iconLabel.setAlignment(Qt.AlignCenter)
+ self.m_iconLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.verticalLayout.addWidget(self.m_iconLabel)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.m_errorLabel.sizePolicy().hasHeightForWidth())
self.m_errorLabel.setSizePolicy(sizePolicy)
- self.m_errorLabel.setAlignment(Qt.AlignCenter)
+ self.m_errorLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.m_errorLabel.setWordWrap(True)
self.verticalLayout.addWidget(self.m_errorLabel)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.m_infoLabel.sizePolicy().hasHeightForWidth())
self.m_infoLabel.setSizePolicy(sizePolicy1)
- self.m_infoLabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignVCenter)
+ self.m_infoLabel.setAlignment(Qt.AlignmentFlag.AlignLeading|Qt.AlignmentFlag.AlignLeft|Qt.AlignmentFlag.AlignVCenter)
self.m_infoLabel.setWordWrap(True)
self.verticalLayout.addWidget(self.m_infoLabel)
self.buttonBox = QDialogButtonBox(CertificateErrorDialog)
self.buttonBox.setObjectName(u"buttonBox")
- self.buttonBox.setOrientation(Qt.Horizontal)
- self.buttonBox.setStandardButtons(QDialogButtonBox.No|QDialogButtonBox.Yes)
+ self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
+ self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.No|QDialogButtonBox.StandardButton.Yes)
self.verticalLayout.addWidget(self.buttonBox)
################################################################################
## Form generated from reading UI file 'downloadmanagerwidget.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
"}")
self.m_topLevelLayout = QVBoxLayout(DownloadManagerWidget)
self.m_topLevelLayout.setObjectName(u"m_topLevelLayout")
- self.m_topLevelLayout.setSizeConstraint(QLayout.SetNoConstraint)
+ self.m_topLevelLayout.setSizeConstraint(QLayout.SizeConstraint.SetNoConstraint)
self.m_topLevelLayout.setContentsMargins(0, 0, 0, 0)
self.m_scrollArea = QScrollArea(DownloadManagerWidget)
self.m_scrollArea.setObjectName(u"m_scrollArea")
" margin: 2px;\n"
" border: none;\n"
"}")
- self.m_scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
- self.m_scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self.m_scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
+ self.m_scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.m_scrollArea.setWidgetResizable(True)
- self.m_scrollArea.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
+ self.m_scrollArea.setAlignment(Qt.AlignmentFlag.AlignLeading|Qt.AlignmentFlag.AlignLeft|Qt.AlignmentFlag.AlignTop)
self.m_items = QWidget()
self.m_items.setObjectName(u"m_items")
+ self.m_items.setGeometry(QRect(0, 0, 382, 208))
self.m_items.setStyleSheet(u"#m_items {background: palette(mid)}")
self.m_itemsLayout = QVBoxLayout(self.m_items)
self.m_itemsLayout.setSpacing(2)
sizePolicy.setHeightForWidth(self.m_zeroItemsLabel.sizePolicy().hasHeightForWidth())
self.m_zeroItemsLabel.setSizePolicy(sizePolicy)
self.m_zeroItemsLabel.setStyleSheet(u"color: palette(shadow)")
- self.m_zeroItemsLabel.setAlignment(Qt.AlignCenter)
+ self.m_zeroItemsLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.m_itemsLayout.addWidget(self.m_zeroItemsLabel)
################################################################################
## Form generated from reading UI file 'downloadwidget.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
def setupUi(self, DownloadWidget):
if not DownloadWidget.objectName():
DownloadWidget.setObjectName(u"DownloadWidget")
+ DownloadWidget.resize(144, 103)
DownloadWidget.setStyleSheet(u"#DownloadWidget {\n"
" background: palette(button);\n"
" border: 1px solid palette(dark);\n"
"}")
self.m_topLevelLayout = QGridLayout(DownloadWidget)
self.m_topLevelLayout.setObjectName(u"m_topLevelLayout")
- self.m_topLevelLayout.setSizeConstraint(QLayout.SetMinAndMaxSize)
+ self.m_topLevelLayout.setSizeConstraint(QLayout.SizeConstraint.SetMinAndMaxSize)
self.m_dstName = QLabel(DownloadWidget)
self.m_dstName.setObjectName(u"m_dstName")
self.m_dstName.setStyleSheet(u"font-weight: bold\n"
################################################################################
## Form generated from reading UI file 'passworddialog.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.0
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
self.gridLayout.setObjectName(u"gridLayout")
self.m_iconLabel = QLabel(PasswordDialog)
self.m_iconLabel.setObjectName(u"m_iconLabel")
- self.m_iconLabel.setAlignment(Qt.AlignCenter)
+ self.m_iconLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.gridLayout.addWidget(self.m_iconLabel, 0, 0, 1, 1)
self.m_passwordLineEdit = QLineEdit(PasswordDialog)
self.m_passwordLineEdit.setObjectName(u"m_passwordLineEdit")
- self.m_passwordLineEdit.setEchoMode(QLineEdit.Password)
+ self.m_passwordLineEdit.setEchoMode(QLineEdit.EchoMode.Password)
self.gridLayout.addWidget(self.m_passwordLineEdit, 2, 1, 1, 1)
self.buttonBox = QDialogButtonBox(PasswordDialog)
self.buttonBox.setObjectName(u"buttonBox")
- self.buttonBox.setOrientation(Qt.Horizontal)
- self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
+ self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
+ self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok)
self.gridLayout.addWidget(self.buttonBox, 3, 0, 1, 2)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from functools import partial
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWidgets import QLineEdit, QSizePolicy, QWidget, QVBoxLayout
from PySide6.QtGui import QAction
self._fav_action = QAction(self)
self._view = view
- self.setAttribute(Qt.WA_DeleteOnClose)
- self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
+ self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
+ self.setSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Minimum)
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
self._view.setFocus()
self._url_line_edit.setReadOnly(True)
- self._url_line_edit.addAction(self._fav_action, QLineEdit.LeadingPosition)
+ self._url_line_edit.addAction(self._fav_action, QLineEdit.ActionPosition.LeadingPosition)
self._view.titleChanged.connect(self.setWindowTitle)
self._view.urlChanged.connect(self._url_changed)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from functools import partial
self.handle_register_protocol_handler_requested)
old_page.fileSystemAccessRequested.disconnect(self.handle_file_system_access_requested)
- self.create_web_action_trigger(page, QWebEnginePage.Forward)
- self.create_web_action_trigger(page, QWebEnginePage.Back)
- self.create_web_action_trigger(page, QWebEnginePage.Reload)
- self.create_web_action_trigger(page, QWebEnginePage.Stop)
+ self.create_web_action_trigger(page, QWebEnginePage.WebAction.Forward)
+ self.create_web_action_trigger(page, QWebEnginePage.WebAction.Back)
+ self.create_web_action_trigger(page, QWebEnginePage.WebAction.Reload)
+ self.create_web_action_trigger(page, QWebEnginePage.WebAction.Stop)
super().setPage(page)
page.create_certificate_error_dialog.connect(self.handle_certificate_error)
page.authenticationRequired.connect(self.handle_authentication_required)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 WebEngineWidgets Example"""
--- /dev/null
+Minibrowser Example
+===================
+
+Simple application that demonstrates how to use a QWebView modules with Qt Quick.
+
+.. image:: minibrowser.webp
+ :width: 800
+ :alt: Minibrowser screenshot
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import sys
+from pathlib import Path
+
+from PySide6.QtCore import QCoreApplication, QUrl, QRect, QPoint
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQml import QQmlApplicationEngine
+from PySide6.QtWebView import QtWebView
+import argparse
+
+import rc_qml # noqa: F401
+
+
+class Utils:
+ @staticmethod
+ def fromUserInput(userInput):
+ if not userInput:
+ return QUrl.fromUserInput("about:blank")
+ result = QUrl.fromUserInput(userInput)
+ return result if result.isValid() else QUrl.fromUserInput("about:blank")
+
+
+if __name__ == "__main__":
+ QtWebView.initialize()
+ app = QGuiApplication(sys.argv)
+ QGuiApplication.setApplicationDisplayName(QCoreApplication
+ .translate("main", "QtWebView Example"))
+
+ parser = argparse.ArgumentParser(description=QGuiApplication.applicationDisplayName())
+ parser.add_argument("--url", nargs="?",
+ default="https://www.qt.io",
+ help="The initial URL to open.")
+ args = parser.parse_args()
+ initialUrl = args.url
+
+ engine = QQmlApplicationEngine()
+ context = engine.rootContext()
+ context.setContextProperty("utils", Utils())
+ context.setContextProperty("initialUrl", Utils.fromUserInput(initialUrl))
+
+ geometry = QGuiApplication.primaryScreen().availableGeometry()
+ if not QGuiApplication.styleHints().showIsFullScreen():
+ size = geometry.size() * 4 / 5
+ offset = (geometry.size() - size) / 2
+ pos = geometry.topLeft() + QPoint(offset.width(), offset.height())
+ geometry = QRect(pos, size)
+
+ engine.setInitialProperties({"x": geometry.x(), "y": geometry.y(),
+ "width": geometry.width(), "height": geometry.height()})
+ qml_file = Path(__file__).parent / "main.qml"
+ engine.load(QUrl.fromLocalFile(qml_file))
+
+ if not engine.rootObjects():
+ sys.exit(-1)
+
+ exit_code = app.exec()
+ del engine
+ sys.exit(exit_code)
--- /dev/null
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtWebView
+import QtQuick.Layouts
+
+
+ApplicationWindow {
+ id: window
+ visible: true
+ title: webView.title
+
+ menuBar: ToolBar {
+ id: navigationBar
+ RowLayout {
+ anchors.fill: parent
+ spacing: 0
+
+ ToolButton {
+ id: backButton
+ icon.source: "qrc:/left-32.png"
+ onClicked: webView.goBack()
+ enabled: webView.canGoBack
+ Layout.preferredWidth: navigationBar.height
+ }
+
+ ToolButton {
+ id: forwardButton
+ icon.source: "qrc:/right-32.png"
+ onClicked: webView.goForward()
+ enabled: webView.canGoForward
+ Layout.preferredWidth: navigationBar.height
+ }
+
+ ToolButton {
+ id: reloadButton
+ icon.source: webView.loading ? "qrc:/stop-32.png" : "qrc:/refresh-32.png"
+ onClicked: webView.loading ? webView.stop() : webView.reload()
+ Layout.preferredWidth: navigationBar.height
+ }
+
+ Item { Layout.preferredWidth: 5 }
+
+ TextField {
+ Layout.fillWidth: true
+ id: urlField
+ inputMethodHints: Qt.ImhUrlCharactersOnly | Qt.ImhPreferLowercase
+ text: webView.url
+ onAccepted: webView.url = utils.fromUserInput(text)
+ }
+
+ Item { Layout.preferredWidth: 5 }
+
+ ToolButton {
+ id: goButton
+ text: qsTr("Go")
+ onClicked: {
+ Qt.inputMethod.commit()
+ Qt.inputMethod.hide()
+ webView.url = utils.fromUserInput(urlField.text)
+ }
+ }
+
+ ToolButton {
+ id: settingsButton
+ icon.source: "qrc:/settings-32.png"
+ onClicked: {
+ settingsDrawer.width = (settingsDrawer.width > 0) ? 0 : window.width * 1/4
+ }
+ Layout.preferredWidth: navigationBar.height
+ }
+
+ Item { Layout.preferredWidth: 10 }
+ }
+ ProgressBar {
+ id: progress
+ anchors {
+ left: parent.left
+ top: parent.bottom
+ right: parent.right
+ leftMargin: parent.leftMargin
+ rightMargin: parent.rightMargin
+ }
+ height:3
+ z: Qt.platform.os === "android" ? -1 : -2
+ background: Item {}
+ visible: Qt.platform.os !== "ios" && Qt.platform.os !== "winrt"
+ from: 0
+ to: 100
+ value: webView.loadProgress < 100 ? webView.loadProgress : 0
+ }
+ }
+
+ Item {
+ id: settingsDrawer
+ anchors.right: parent.right
+ ColumnLayout {
+ Label {
+ text: "JavaScript"
+ }
+ CheckBox {
+ id: javaScriptEnabledCheckBox
+ text: "enabled"
+ onCheckStateChanged: webView.settings.javaScriptEnabled = (checkState == Qt.Checked)
+ }
+ Label {
+ text: "Local storage"
+ }
+ CheckBox {
+ id: localStorageEnabledCheckBox
+ text: "enabled"
+ onCheckStateChanged: webView.settings.localStorageEnabled = (checkState == Qt.Checked)
+ }
+ Label {
+ text: "Allow file access"
+ }
+ CheckBox {
+ id: allowFileAccessEnabledCheckBox
+ text: "enabled"
+ onCheckStateChanged: webView.settings.allowFileAccess = (checkState == Qt.Checked)
+ }
+ Label {
+ text: "Local content can access file URLs"
+ }
+ CheckBox {
+ id: localContentCanAccessFileUrlsEnabledCheckBox
+ text: "enabled"
+ onCheckStateChanged: webView.settings.localContentCanAccessFileUrls = (checkState == Qt.Checked)
+ }
+ }
+ }
+
+ WebView {
+ id: webView
+ url: initialUrl
+ anchors.right: settingsDrawer.left
+ anchors.left: parent.left
+ height: parent.height
+ onLoadingChanged: function(loadRequest) {
+ if (loadRequest.errorString)
+ console.error(loadRequest.errorString);
+ }
+
+ Component.onCompleted: {
+ javaScriptEnabledCheckBox.checkState = settings.javaScriptEnabled ? Qt.Checked : Qt.Unchecked
+ localStorageEnabledCheckBox.checkState = settings.localStorageEnabled ? Qt.Checked : Qt.Unchecked
+ allowFileAccessEnabledCheckBox.checkState = settings.allowFileAccess ? Qt.Checked : Qt.Unchecked
+ localContentCanAccessFileUrlsEnabledCheckBox.checkState = settings.localContentCanAccessFileUrls ? Qt.Checked : Qt.Unchecked
+ }
+ }
+}
--- /dev/null
+{
+ "files": ["main.py", "main.qml", "qml.qrc", "images/left-32.png", "images/right-32.png",
+ "images/refresh-32.png", "images/settings-32.png", "images/stop-32.png"]
+}
--- /dev/null
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ <file alias="left-32.png">images/left-32.png</file>
+ <file alias="stop-32.png">images/stop-32.png</file>
+ <file alias="refresh-32.png">images/refresh-32.png</file>
+ <file alias="right-32.png">images/right-32.png</file>
+ </qresource>
+</RCC>
--- /dev/null
+# Resource object code (Python 3)
+# Created by: object code
+# Created by: The Resource Compiler for Qt version 6.7.2
+# WARNING! All changes made in this file will be lost!
+
+from PySide6 import QtCore
+
+qt_resource_data = b"\
+\x00\x00\x02\x7f\
+\x89\
+PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
+\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\
+\x00\x00\x00\x06bKGD\x00i\x00\xa1\x006za\
+\x0c\x8d\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\
+\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\
+\xdf\x01\x1a\x09+7\xecd\xf9\xf8\x00\x00\x02\x0cID\
+ATX\xc3\xcd\x97\xb1K#A\x14\xc6\xbfy\xbb\x90\
+\x85\xa4\xb0\x91\x03\x8b\x88WX\xdc),\xd8X\xad6\
+f\x09X\xab\x95r\x049\xb0\xd2\x08\xfe\x11\xa2I\xe0\
+@\x10\x11\xb1\x10\xf4\x8a\x03+1\x07G\xd8\xea@\x02\
+)\xbc+,\x84\xa4\x10\xd16\xc2.$3\x16\xee\x84\
+\xf5\xdcpF7\x93L\xb9;\xcb\xef{\xdf\xbc\xf7\xf6\
+\x0d\xd0\xe3\xc5:\xd9\xbc\xbff\x99\x86\xc7\x96\x12\x0f\x94\
+NT\x1b\xa3h\xfa\xdfk\x10\xf5a\xfd\xaa\x1e\xe7g\
+nL\x1cf\xf2N%2\x01{\xeb\x16\x19\x1e\xcb\x0e\
+]\xf2\xcd\x16\xf0\x7fK\x83\xb8\x19\xa3\x0d7&r\xcb\
+\xdb\x0e\x7f\xb3\x80\x83Uk&Y\x11\xe7\xaf\x06\x87\x08\
+\xa9\x99\xcc\xfeRp~\xb6\xdbB\xed^\x1c\xadL\xe5\
+\x93eQ|3\x1c\x00\x9a`\xc9\xb2(\x1e\xadL\xe5\
+;r\xe0\xf8\xeb\xf4\xc9\xe0\xdf\xe6\x5c\x94\xc9v\xffI\
+\xfb\xbe\xb0[\x9a\x7fiRH\xe4\x1f\xfe\xf0L\xd4\xd9\
+\x1e\xbf\x17\x9f\xed\xd9\x91\x81\x1f\x17\xd5\xf3\xb6\x0e\x1c\xac\
+Z3\xc9\xb2(v\xb3\xecj\x13,\x15\xcc\x09\x16\xcc\
+\xf6\x8fe\xd1x\xd7\x99\xbf21\xaf'\x98.\xabC\
+\x97\xcf\x0d\x8fe\xd1\x14\xa1p2\x0cL\x17;3\xa6\
+\x94J\x81\xbbnhb\x1a\x1e\xcb\x02\xd8zV\x05C\
+\x97|SU\xf7\x0b\xb2Hv\xb8\xae[\xff\x8f\x0b\xfb\
+k\x96\xd9\x12`xlI\xf5?@2\x09\x00\x12\x0f\
+\x94V-@2\x9f\x04T\x1b\xa3\xca\x05\xf8L\x92g\
+\xa2\xfc?\xec3\xa9\xd7\xf3\x00\xc9\xe6\xa0\x9c\xec3\x09\
+\x00\xea\xc3\xfa\x95j\xbed>\x09\x88\xf33\xe5\x02|\
+&\x01\x80\x1b\x13\x87\xaa\x05H&\x01@&\xefT\x94\
+\xe6\x81\x06!\xe7\xc6V\x15\xdc\x8c\xd1\x86*~\x90\xa5\
+\x07,\xc9AC\xe8\xe0\xc9]\x17\xbf,+\xb2\xe8\xdd\
+\x98\xc8\xbd\x98\x09\x97\xb7\x1d^3\x99\xdd\xed\xe8k&\
+\xb3\x83\x93\xf2\xb3\x91\xec\xf4w\xf5\xda\x9e\x1d\x19H\xdc\
+\x89\xc9n\xc0o\xc7\xa9\xb0\xf8\xcd\xd9\xe9\xab\xa14\xb4\
+\x15/\xec\x96\xe6o\xc7\xa9\x10e\xe4a\xf0\xbe\xb8\x98\
+\xf4\xf7\xd5L\xc5\xe5\xb4\xe7\xeb\x11\x07R\xed#?\x12\
+G\x0e\x00\x00\x00\x00IEND\xaeB`\x82\
+\x00\x00\x05\x15\
+\x89\
+PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
+\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\
+\x00\x00\x00\x06bKGD\x00\xd9\x00M\x00M\x0d\x89\
+\x85\xe9\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\
+\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\
+\xdf\x01\x17\x0f!*\x83j\xd9\xc4\x00\x00\x04\xa2ID\
+ATX\xc3\xcd\x97[L\x93g\x18\xc7\x7f_)\x05\
+Z\x0a\x149\xaa\x84\x83\xe0&\xa2\x11g\xc0L\xdc\x92\
+ZL\x06\xd9\xc6\x95\xcb\x8e\xbd\xf1b\xa0u\xa7\xbb\x1d\
+b\xdc\xc5\xee\xb69\xaa\xf4\xc6,a\xd9\x5cf\xb2\xc4\
+]\xa8\x09\x87&\x1b\x90(q\xe0\x1c\xe2\x06(8\x0f\
+\x9c\x84ZX\x81r\xe8\xbb\x0b\xfbu\xdf\xd7\x16\xf8\x98\
+K\xf4M\x9a4\xcf\xfb\xbc\xcf\xff\xff>\x87\xf7y>\
+x\xccKZ\x8brmcY6\xb0\x17\xa8\x04*\x80\
+\xbc\xe0\xd6\x10\xd0\x0e4\x03m.{\xe7\xf0\xffF\xa0\
+\xb6\xb1L\x02\xac@=P\xac\xd1n/p\x04p\xbb\
+\xec\x9d\xe2?\x13\xa8m,+\x02\xce\xae\x018\x1a\x91\
+\x1a\x97\xbd\xb3\x7f9\x05\xdd\x0a\xe0\x07\x80\xbeG\x00'\
+x\xb6/hK\xbb\x07j\x1b\xcb\x0e\x01'\xc2\xe5\xa9\
+\xa6,Js\xad\x14e\xee$;9\x1fS\x5c\x0a\x82\
+\x00\xd3s\x1e\xeez\x06\xb8~\xef\x22]C-\xcc-\
+\xceD3{\xd8e\xef<\xb9*\x81 \xdb\x1f\x94\xb2\
+\xac\xe4<^.\xad\xa3dc\x05:)\xd2i\xb3\xf3\
+>\xbc\xb3\xe3H\x92\x8e\xf8X\x13?\xffq\x86\x96\xde\
+\xefX\x0a,\x86\xab\xbe\xe2\xb2w\x9eY\x96@0\xe6\
+}\xffnJT\x96\xbcE\xf5\xf6\x83\xc4\xea\x0d*K\
+\xfe\x85Y\xda\xfb\xcfr\xf1\xc69\xeey\x06\x10<\xcc\
+5\x9d\x14C~\xfa66Z\x8a\xb8<\xd4\x84\xcf\xef\
+\x0d'\xb1Y\x99\x13RX\xb6\xf7\xc81\x97\x90x\xf3\
+\xd9O\xd8]X\x0d\x80\x10\x02I\x92\x10B08\xde\
+\xc3\xd7m\x1f3\xe9\x1bY1\x01L\x86d|\xf3\xde\
+h\x89Y\x22W\x87\xd2\x9fVe\xc2\xd5\xec<L\xf9\
+\xa6*\x84\x10\x08!B$\xfaF~\xe5xS\xdd\xaa\
+\xe0@4p91\xad\xd1\xaa\xa0^\xfe\xb3)c\x07\
+\xd6\xe2W\x91$I\xf5\xf3\xceNp\xea\x97\x0fY\x0c\
+\xcck.\x03\x83>!\x9a\xb8^E \xf8\xc2\x85n\
+\xffR\xe9\xdb\xc4\xe8bB7\x97o\x7f\xfe\xb7S\xd1\
+b\xba\xec*H\xdf\xce\xa75?\x92\x9f\xb6-\xc2\x0b\
+A\xcc\x90\x07\xf6\xca;\x99I\xb9\x14f\xecP\xb9]\
+\x08\xc1\xfc\xe2\x1c\x97n\x9e\xd7\x0c\x9e\x93\xfa\x14\xb5\xd6\
+\xcf1'\xa4R\xb7\xef\x0b6X\x8a\xc2U\xf6*\x09\
+T\x86R4kWT\x837\xc7\xaf\xb2\xb0\xe4\xd7\x04\
+\x9e\x99\x94\xcb!\xeb\x97\x18\x0df\x00\x8c\x063\x8e}\
+_\x91n\xceQ\xaaU*\x09T\xc8\xd2\x8c\xa4\x9c\x88\
+\xd8K\x92\xc4\xf8\xf4\x1dM\xe0\xa9\xa6,\x1c6'I\
+\xc6u\xaa\xf3I\xc6u\x1c\xa9tb1f\xa2\xc4\x94\
+\x09\xe4\xed\xdb\xf2\x1a\xf6=G)\xd9\xb0G\xe5~y\
+\x15f\x94b\xdfs\x14[\xf1\x1b\xcb\x82\x9b\xe3Sq\
+\xd8\x9cXL\x19\xaa\xea\x91m\xadK\xcc\xc6Q\xe9$\
+1\xce\x82\xdcICUp\xe9\xe6\x05r\xd3\xb6\x92\x91\
+\x94\xa3\x02\x97\x0de\xa7\xe4\x93\x97V\xb2b\x1e\x94\x17\
+T1t\xbf\x87\xe1\x07\x83*`\xd9\xd6\xa8\xf7/n\
+\xdd\xbfFy\xc1\x0b\x11\xcdh\xe8o\xbf\x07g\xb3\x83\
+I\xdfH\xd4\x10x|c8[\x1cL\xcfM.K\
+\xa0\xa5\xf7[\x1a;\x8e10\xd6\x1d\xd5F\xcf\xdd\x0e\
+\x1a;\x8e\xd1z\xfd\xb4<C\x84\x08\xb4\x03xfF\
+\xa9ov053\xa1\xf2\xc2\xd4\xcc\x04\xce\x16\x87\xa6\
+\xc7\x07 \xdd\xbc1B&\x84`l\xea\xb6R\xd4\xae\
+$\xd0,K\xc7\xa7o\xe3l}\x07\x9f\x7f\xea\xe1k\
+\xe6\x9f\xe2\xa4\xfb=F\xa7ni\x02\x8f\x8d\x89\xa3 \
+}{\xc8\xf5\xca\x10\xf4\x8d\x5cV\xaa6+\x09\xb4)\
+w\xeez\xfaih}\x9f\xe9\xd9I\x5c\xee\x0f\xb8=\
+\xf9\xa7\xe6\xfa//\xa8\xc2\xa0\x8f\x8f\x90\x0f\x8c]\x09\
+\xbfD[x3\xba\x16>|\x18\xf4\x09\xcc/\xcej\
+\x067\xc5%\xf3\xd1\x8b\xa7I1\xa6\xa9\xe4K\x81%\
+\x8e7\xd5qc\xecJ\xa8!\xb9\xec\x9d[\xc3{\xc1\
+\x91p\x83k\x01\xd7\xeb\x0c\x1c|\xee3R\x8ci\x11\
+\xaew\xf7~\xaf\x04Wa)\x09\xb8\x83\xad2\xa2\xa5\
+jy|\xde\xdd\xdf\xc0\xe6\xacgT\xb5\x0fpq\xe0\
+\x1cg\xbbN\x84\xb7c\xb7\xa6\x81Dv\xeb\xae\xbc\xfd\
+\xdc\xf1\xf438\xfe;\x01\xb1\x14\x1aV\xd6[\x0a\xd9\
+\xbd\xa9\x9a\x8a\xa2\x1a\xe2b\x13T3\xc3\xe2\xd2\x02\xe7\
+\xae\x9e\xa2\xb9\xe7\x9b\xd0\xb0\xb2\xe2@\xb2\xd2H\x16\xa3\
+\xd3c+~\x9d\xe7\x9f>\xc0\xdc\x82\x0f!\x02$'\
+\xa4c\x8cK\x8c(\xb5\x80\x08\xd0s\xa7\x9d\x9f\xba\x1b\
+\x18\xf1\x0e\xadm$[m(\x8d\xd7\x1b\xd9\x99gc\
+\xcb\xfa\xddl\xb0\x14b\x8e\xb7 \xa1\xc3\xe7\x7f\xc0\xb0\
+w\x90\xfe\xd1.\xbao\xb9\x97{/\xb4\x0d\xa5+y\
+\xe2\x11V\xc4\xcd\x9f\xfc\x0f\x13\x80\xe0\xc1\x12\xc0\x16\xad\
+BV\x01\xb6\x05\x87\xcf\xfe'\xfa\xe3\xf4\xb1\xaf\x7f\x00\
+\x9e\xe8\x03h^\xfe7\xb1\x00\x00\x00\x00IEND\
+\xaeB`\x82\
+\x00\x00\x03?\
+\x89\
+PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
+\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\
+\x00\x00\x00\x06bKGD\x00i\x00\xa1\x006za\
+\x0c\x8d\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\
+\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\
+\xdf\x01\x17\x0f)\x18\x83d\x02L\x00\x00\x02\xccID\
+ATX\xc3\xcd\x97\xcfKTQ\x14\xc7?o&\x89\
+\x22P\xc8~)\xd9\xd0/$tF\x03g\xe5d\x98\
+#I?\xd0\x8d\xd2\x22'\xc5\xa2A\xe9\x0f\xe8\xc7\xa6\
+ h;\x10\x13\x96\xc6\xe0J7\x0dd\xb8\x18\x1c\xc4\
+\xb1\xcdc\xcaQ$r\x5c\xe4 \xa6\x1b\x17B\xe0F\
+}-z\xef\xf1\xe6\xbd\xf9\xf1\xde\xa8\xe9Y\xbdw\xee\
+\xb9\xf7\xfb\xe5\x9c\xef\xbd\xf7\x5c\xd8g\x13\xac\x04\xfbC\
+\xee3\x80\x07\xf0\x02\xf5\x80C\x1eZ\x04\xa6\x80\x08\x10\
+\x0b\xfa\xc4\x95]#\xe0\x0f\xb9\x05\xa0\x11\x08\x00WL\
+\xae\xfb\x03x\x02D\x83>Q*\x98\x80?\xe4\xbe\x04\
+\x84-\x00g\x22\xd2\x1a\xf4\x89\x0b\xd9\x02l9\xc0\xdb\
+\x81\xe4\x0e\xc0\x91\xe7&\xe5\xb5\xcc\x13\xf0\x87\xdc\xbd\xc0\
+\xf0.jmX^3\x7f\x09d\xb6\xc3{$\xfa\x8e\
+\xa0O\x1c\xc9J@\xaey2\xdf*\xd7+\xdb)=\
+Vn\xf0\xcf-\x7f\xe5\xe7\x8a\x98o\xfae\xad&\x04\
+\x9d\xda\xe7\xf2\xd5\xbc\xb5\xb6\x17o\xd5}\x04!=y\
+\xd3\xa9(\x03\x93\xcf\xd9\x96\xb6\xcc\x08\xb3J\xd9\x1dZ\
+\x0d4\x9a\x01o\xae\xee4\x80'R\x13\x0c\xc6^\x98\
+\x01W\x84\xd9\x98I\x84\x81\x5c\xb3\xee\xd6\xfai\xae\xee\
+4\xf8\x13\xa9\x09\x06b\xcf\xd8\xda\xde\xb4\xa2\x85@Z\
+\x09\xe4\x13\xeew\xb6\xe8;5\x8f\xb9Y\xfd\xc0\xe0\x9f\
+]\x9a\xe4\xc3\xe4S\xab\xe0\x8a\x95\x05}\xe2\x8a\x92\x01\
+\xcf\x7f\x06W1\x0f\xc9?\xdeL\x11\xb7]\x8fTp\
+A\x10\x90\xa4\x7f\xa7\xea\xfa\xc6\x1a\xdfS\xe38\xcf6\
+XB\x9cN\x8dk\x7f\xbd\xc0\x88B\xa0>\x13x\x8b\
+\xb3;Mp\xcaw\xc9\xd1R\xba</-\x81K\x92\
+D\xefP\x1a\x81zm\x06\x1c\xda\x91[\xae\x87\xb48\
+\xbb\xd5\x89\xfa\x0c\xe8\xffs\x8di\x89\xeb\xcc\xa1%P\
+\xd8].\x08;>\x1am\x9a\xfb\x5c\xb5/3\xef\x19\
+\x9b\x1dTA\x14 \xedw6Bfc\x15L%\x03\
+S@\xa5vtt\xa6\x1f@-\x85\xd6\xd67\xd6\xf8\
+\xf4-\xc0f\xe1;@\xc1T\x09D\x80\x1e}\xc4\xe8\
+L?\x82`\xa3\xc5\xd9\x95\xe6/>r\x9c\xab\xe7n\
+\xect\x1bF\xb4%\x88e\x8b\xfa\x9cx\xc7\xd8\xecG\
+C\xaa]\x15\x0d\xf4\x5c{\x8d\xddV\xb0\x8cb\x00v\
+\x80xx\xf9O][y;p\x22Sdr5\x8e\
+\xddV\xc4\xc5S5i\xfe\xd3\xc5\x0e\xca\x8a/\x90X\
+\x9a@\x92\xb6-uJA\x9f\xf8F%\x00P\xd7V\
+>\x0ftf\x9b1\xbf\x1a\xa7\xc8v\x98\xf3'\x9dF\
+\x12%\x96I\xdc\x8b\x87\x97\x7f\xe9/\xa3\xa8|Uf\
+\xb5\xf0\xf4[\x22sC\x06\xbf\xab\xa2\x81n\xcf+l\
+\x82\xddl\x9f\x18=x\x0d\xc9~\xb5d\x86\xa6T\x0e\
+\xe8\xdb\x03\xf0>=x\xcew\xc1.g\xa2#\x13\xf8\
+\xc1~\x98\xc8\xe5X\x00\xaa\x80\xa6|;$\x03p\x93\
+\xdc|.\x1c\xe8\xc7\xe9\xbe\xdb_E\x87\x0e'\xe81\
+\xfc\xef\x00\x00\x00\x00IEND\xaeB`\x82\
+\x00\x00\x04\xcf\
+\x00\
+\x00\x141x\xda\xc5X\xddS\xe36\x10\x7f\xf7_\xa1\
+\xe6\xe1&\xe9L\x1c>\xae\xd3\x99\xb4\xf4\x06B\xb9\xa3\
+\x93\x9b\x03\x02\xe5\xfa(l%VQ$#\xc9\x04z\
+\xbd\xff\xbd+\xc9vl\xd9\x0e\xa1\xf4\xa8\x9f\xa2\xdd\xf5\
+\xee\xea\xb7\x9f\xceh\x84&\x22}\x94t\x91h\xd4\x9f\
+\x0c\xd0\xde\xce\xee\x8f\xe82!\xe8\x5c\x03g\x99b\xfe\
+\x88\xa6:\x0e\x83\xd1\x08\xcd\xce\x8e?\x0f\xa74\x22\x5c\
+\x91\xe1iL\xb8\xa6sJ\xe4\x18\xe5\xb4\x0b2\x1f\x9e\
+\xeb!\xbc\xb6$2\xa2\x98\xa1O\x17\xe8hv<\xdc\
+\x1fN\x18\xce\x14\x09\x02\xbaL\x85\xd4\xa0\xfc<\xa3\xd1\
+\xadw\x0c'\x82k)\x98Z\xd3\xaf\xc9\xcd\xef\x94\xac\
+|\xc1)~\x14\x99VA\x10\x1c\xa6)\xa3\x11\xd6T\
+\xf0k\xcac\xb1B_\x02\x04\x0f\x8d\xc7he\x09\xf6\
+xO\x15\xbdad\x8c\xb4\xcc\x88\xa5<\x8c\x11\xe5T\
+\x83\x97\x9f\xed\xf9\xb1<\xffa\xcf+\x1a\xeb\xa4\xa4]\
+\x9b\x93\xa5'\xc4`U2>\xd8\xa3\xe5h\xaa\x8d\x85\
+\x95s9\xb4\xc7\xc0r\x96\x84gG\x18\x80\xba\x14\x82\
+\xc1\x8f\xdc\xc7\xc2O\x8e\xef\xe9\xc2^\x01x%\xe7B\
+\xac\xdc5+\xd2\xe6\xc1<J\x84T\xe1\x9c26F\
+)\x96\x10\x87\x9a\x80JqD\xf9b\x8cv\x82\x1a\xdd\
+\x1a\xcf\xb4\x16\xdc\xd3X\xf8q\x83\xa3['\xd0dG\
+\x82\x87Jd2\x82\x1b\xf6\xeed4\x1e12\xd7\xc3\
+\xfd\xbd0\xe5\x8b^C^\xf0\x09\x84\xe5\x96\xc4k<\
+\x16\xe2\x08\xf4\xf7\x07\x0dY\xc21\x84\xa6\x22\x19a\xfe\
+\xde\x0a7D\x1d a*\xc9\x9cHI\xe2k\x17\xa4\
+\x1a\x80a\xb2\x0eI\xf1|}&\x12s!WX\xc6\
+\xdb\x83a\x0b\xe8yh\x9c8\x1b[\x03\x92\xcb\xffo\
+\x98H\xc2\x04\xde\x0a\x92\xc2m#\x0f\x89\x88\xde\xe5 \
+)-\xd2\x02#T\x22G\xe6\x92\xa8\xe49\xd8\xad\xf5\
+\x16\x14\xa3\xb9?@k\x11\xe7l\x0b\xb6\xff\x11Z\xa7\
+\x9a,\xd1\x97.m?4\xc0%\x0f\xfa\x84\x12\x16\xb7\
+`\x9b\xeb0\xe5\x9c\xbf^\xf6(?\x04\x99dVK\
+\x93\xc9\xd3L\x7f$:\x11\xf1\x07\xca\xb5\x1aC\xa7\x0c\
+O\x97\xc9\x95d\x93\x04K\x1ci\x22\xd5'\xce\x1e\xd1\
+\xdf9\xe7\xcc\xba<\x15+\xe8\xd4X5\xcdi\xf0x\
+\x0d'\x18n\x09\xcca\x14\x91TW#\x03r\xe8\x00\
+e\x9a2\xe8OR,\xaf\x14\x91\xa7\xc6\xb7\xbe\xd1\xe7\
+E\xe3\x85\x90>\x95\xaf\xd0o\xdas\xd5]\xedN]\
+\xca~\xef\xbd\xe8\x0d6\xa5\x5cS\xb5y\x00\xc2\x0a\xe2\
+a\x04\x03\x8f\xea\x96dk\x91MhL:$\x9f\x06\
+\xb1\x88\x7f\xd8\x82\xa6\x05\xf4%\xf5\xad\x88\xd6PUj\
+\xfb\xa6W\xbc\xb1M\xed\xb6\x03Yh8\x96\x18\x121\
+\xb43\x17\xee\xdeo\xa5\xff\x82v\x06P\xf3;\xa8\x98\
+\xec9\xfd{\xb4;z\xfb\x04\x18\xafV\xfa\xbb;U\
+\xcb\x95\x9fgR,\xa0\xd1\xa9\xfa\xf8/\xd1Osv\
+\x9d\x93\xcf\xfa\x16\xf4\xcc\xf4-\xa6\x7fh\x0e\xcd4\x17\
+i)p# \xa6\xcb\x86\x88t\xabL.$\x1b7\
+/\x0c}\xc4rAy\xcd\x9c#\xb5k\xf4\xc4+\xb4\
+`S\x88\xf2\xcdj\xbfN\xfd\xcb\xf6\xb2\x94a\x0dC\
+y\x19\x0a\x85\x0e\x0e\x0eP\x0f\xf3X\x0a\x1a\xf7 !\
+\x86\xbb\x90\x11\xc3\xbd\xfakf\x97YH\x91q\xc0\xd6\
+E\xcc\xb3Vn\x84\x9e\xf6\xef\x8cv*T\x0f\xbdy\
+\xd3\xca\x83\xdc\x93\xda\xcbuS\xa6f\xdf\xf2\x02`\xd2\
+\xc1#\xdec\x96y#\xb2\xc8\x0c\xf4\xb3\x11\xafL\xb5\
+\x1a\xb3\xaa\xde]%\xcfKw\xbb\xa0\xad\x94]\xf9\x04\
+\xfe\xe6\xb8)\xec\x13\xc1\xb2%o]<\xa7\xf8\x86\xb0\
+\x96Tt\xfd\xb4\xf7\x1b\xbe\xc7\xb3H\xd2\xd4\x03\xa7\x8e\
+\xfb$!\xb0d\x8a\x87\x8e&\xf4g\xa9\xe4W\xb7\x04\
+\x15\xf2]V\xf3]\xa9\xb5\xf7\x98Wg\x1ak\x023\
+\x90/\xaas\xaa\x00(l\xd83\xdd'*_\x84\x5c\
+39`5\x91x\xb0\xe1^O\x803\x15\x11|\x0e\
+\xc1\x9a\x22\xf1\x82\xbc\x00\x1ff\xf4\xcc\x9c\x9a\xd7A\xa8\
+\xc5\xe27\xc2\xe8\x901\xf8|\x83=\x88 \x0c\x9b\x85\
+R/\xc0\x09\x1b]'\xa0\xea\xd0jz\x1d\xa8<\xa3\
+\xdf4\x95`\x1ak\xa8^\x04\x9f\x069Z\x0e\xb9\xab\
+\x8b\xa9zi~M\x9c\xee\x09\xe6\xee\x22\xe6J\xb0I\
+\xaaWL\xb8.\x17\xfe\x0d\xa6\xb5n\x99\xff\xa1\xe05\
+\xcc\xdc\x89\x92\x06\x8bV\xf9\x85\x7fU\xd9}\xbd\x0e\xea\
+\xad)\xb51\x5c\x88v\x0f\xea\xe2\x9f\x84\x9c\xe7m\x1e\
+\x82O\xddgN\x89\xd4<\xe3\x91YS\xfaf.\x5c\
+\x90\xbb\x8c(=\xf0\xe2H\xe7\xa8\xca\x0ea5\x11r\
+\xa6%\xe8i\xee\x8b\x90CJ0\xe2\x84:_\xfb)\
+hY\x84\xcc_C\x82\x1b\xb7!\xa8\xf0\x9b\x11\xdd\xdc\
+\xf2:\x9byX\x0d!\xda\xd0\x8c\xdfU\xc2\x8b\xec\xa8\
+\xbe\xe2\x91;\xd6,mh\x8b\x1d\xb6\xda\xda\xda\xb6\xd6\
+67\x97\x0e\x83~sx\xd6\xd5\xb6\xac\xc8Mw\xed\
+\xac\xa8m\x1c)J\xe8k\xf0\x0f\x96\x1c\x9e\xf1\
+\x00\x00\x036\
+\x89\
+PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
+\x00\x00 \x00\x00\x00 \x08\x06\x00\x00\x00szz\xf4\
+\x00\x00\x00\x06bKGD\x00i\x00\xa1\x006za\
+\x0c\x8d\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\
+\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\
+\xdf\x01\x17\x0f(,\xbb\xcb\xc7\xb8\x00\x00\x02\xc3ID\
+ATX\xc3\xcd\x97\xbfOSQ\x14\xc7?\xf7\xb5\x8b\
+\x93&j\x94\x10\xa1\x03\x1a\xa2\xa5\x96\x18\xbbH\xab\xa2\
+\x18\x194\xb0\xc8\xa4U`\xb0\x81\xf4\x0f\x908)\xc6\
+\x99\xc4\xd4 \x12\x8a\x13L\xc4\x1f\x01!\x10\xa0L\x0d\
+\x11JIc\xca\x22\x18\xa2\x83##\xf4:\xf8^\xf3\
+\xca{}?\x10\x027\xe9\xd0{\xce=\xdf\xef;\xe7\
+{\xcf\xbd\x17\x0ey\x087\xce\xb1d\xa8\x02\x08\x03M\
+@\x03\xe0SM?\x80\x05`\x0aH%\xa2\xe9_\xfb\
+F \x96\x0c\x09\xa0\x11\xe8\x03.:\x8c\x9b\x03\xe2\xc0\
+L\x22\x9a\x96{&\x10K\x86\xce\x03c.\x80\xcd\x88\
+\xb4$\xa2\xe9\xb5r\x0e\x8a\x05\xf8\x03 \xff\x1f\xe0\xa8\
+k\xf3j,\xe7\x04b\xc9P\x170\xb2\x8fZ\x1bQ\
+c\xda\x97@e;r@\xa2oKD\xd3\xa3e\x09\
+\xa85\xcf[E\xa8\xad\x08\xe1\xaf\xbcf\x98\xff\xb3\xb5\
+\xc9\xec\xf7Q'$.\xe85!v\xa9}\xd5\xae\xe6\
+\x8a\xf0\xd0\x11yI\xb0\xea\xa6\xc16\xb5\xfa\x81\xb1\xa5\
+7N\x84\xe9\xd7v\x87^\x03\x8dN\x04W\x90;\x0c\
+\xa6\x9e\x93\xd9\x983\xd8\x9a\xfc\x0fi\xa9\xefr\x22\xcc\
+F3\x11\xf69-\xe4Na\x9b\xf7\xa9\x1e2\x1bs\
+\x08!J~w\xea\x1eq\xbf>f\x17\xa2\x88\xe5\xd1\
+u\xb8\xd7n\xd4$e\x81\xe5\x9f\xb3T\x9e\xa8\xe1\xec\
+q_\x89\xad\xe6L\x10Ex\xc9\xff^,\xb7\xfc\xf4\
+\xd5\xd6\xca\xfe\xc5\xb1\xcd--\x03\xe1\xbdHz\xa7\xb0\
+\xcd\xc0\xfc32\x1bsHY\xda\xf0\x9a\x03O\xb8\x17\
+|j\xb5<\x0c\xe0\xd5\xca\xa7\xb7\xd4W\xdfrE\xe4\
+\xdb\xfa4\xd5\xa7.q\xfc\xd8\xc9\x92\xf9\xbbu\x8f\x91\
+\xb2\xc0\xe7L\xbf\xd9\xb2&`T#\xd0\xa0\xb7tF\
+z\x11B \xa5D\x08a\x92~\xe3|9\xdf\xe6@\
+;\x80\x19\x89\x06}\x06|\xe5k\xfd/\xb5\x1a!k\
+]\x98\xfb6\x07\xda\x91H\xbed\xde\xe9\xdd}\x96g\
+\x813!J[RvC\xd1\x9d\xe7\xc6>\xad\xdb^\
+V\xff\xedl\xe3+\x83\xbb\xbf\xbe\x88\xa9\x95`\x01\xa8\
+\xd5,\x03\xf3=\xae\xbe\xc2\xabxi\xbd\x12/\x8aP\
+_\x82\xf1\x95\xc1r\x22\x5c\xd0\x13\x98\x02:5\xcb\xd2\
+\xfa\xb4cp\x8f\xe2\xa53\xf2\xaad\x07h\xe0\x13\xd9\
+\xa1r\xe0\x1af\xb1\x04\xa9\xbd\xd4O\x03\x0f\x9c\x8b\x18\
+l\x13\xd9!>-\xbf\xb5Z\x9e*\x12P\xefp9\
+\xb7\xe0\x1d\xe1^.W]7\xb4\xe3\xaf\xabI;\xf0\
+\x9cvo\xd4\xef\x82\xb8[\xf0`\xf5\x0d\x83m2;\
+\xcc\xc7\xa5\x84]\x88\xb8\xd9a4\xe3$\x0b\x8a\xf0\xd0\
+\x1e~a\x00\x97R2\x99\x1dvz\x1c\xcf\x1c\xbd\x0b\
+\xc9a]\xc9\x0c\x9dPu\xe8>\x00\xf0\xee\xdd\xe0\x96\
+\xef\x82}\xceD\x9b\x19\xf8\xd1~\x98\xa8\xe5X\x03\xfc\
+\xc0m\x97}\x22\xa7\xae\xf1[\x81\x1f\x89\xc7\xe9\xa1\x8f\
+\xbfgH\x11\xb4\x13s\xe2\x92\x00\x00\x00\x00IEN\
+D\xaeB`\x82\
+"
+
+qt_resource_name = b"\
+\x00\x0b\
+\x00\x90;'\
+\x00s\
+\x00t\x00o\x00p\x00-\x003\x002\x00.\x00p\x00n\x00g\
+\x00\x0e\
+\x07\xd4\xda\x07\
+\x00r\
+\x00e\x00f\x00r\x00e\x00s\x00h\x00-\x003\x002\x00.\x00p\x00n\x00g\
+\x00\x0b\
+\x00\x80-\xa7\
+\x00l\
+\x00e\x00f\x00t\x00-\x003\x002\x00.\x00p\x00n\x00g\
+\x00\x08\
+\x08\x01Z\x5c\
+\x00m\
+\x00a\x00i\x00n\x00.\x00q\x00m\x00l\
+\x00\x0c\
+\x0fz\xe9\xa7\
+\x00r\
+\x00i\x00g\x00h\x00t\x00-\x003\x002\x00.\x00p\x00n\x00g\
+"
+
+qt_resource_struct = b"\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x01\
+\x00\x00\x00\x00\x00\x00\x00\x00\
+\x00\x00\x00>\x00\x00\x00\x00\x00\x01\x00\x00\x07\x9c\
+\x00\x00\x01\x91\x08\xc1\x9ai\
+\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
+\x00\x00\x01\x91\x08\xc1\x9ai\
+\x00\x00\x00\x1c\x00\x00\x00\x00\x00\x01\x00\x00\x02\x83\
+\x00\x00\x01\x91\x08\xc1\x9ai\
+\x00\x00\x00Z\x00\x01\x00\x00\x00\x01\x00\x00\x0a\xdf\
+\x00\x00\x01\x91\x08\xc1\x9ai\
+\x00\x00\x00p\x00\x00\x00\x00\x00\x01\x00\x00\x0f\xb2\
+\x00\x00\x01\x91\x08\xc1\x9ai\
+"
+
+def qInitResources():
+ QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+def qCleanupResources():
+ QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
+
+qInitResources()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWidgets import QDialog, QLineEdit, QVBoxLayout
{
"files": ["../bindings.h",
+ "../bindings.xml",
"../dialog.py",
"../macros.h",
"../main.py",
"../registerwigglywidget.py",
"../wigglywidget.cpp",
"../wigglywidget.h",
- "../wigglywidget.py"]
+ "../wigglywidget.py",
+ "../CMakeLists.txt",
+ "../../utils/pyside_config.py"]
}
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
from wigglywidget import WigglyWidget
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QBasicTimer, Property
from PySide6.QtGui import QColor, QFontMetrics, QPainter, QPalette
# Copyright (C) 2010 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
import math
super().__init__()
self.pixmap_item = QGraphicsPixmapItem(pix)
- self.pixmap_item.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
+ self.pixmap_item.setCacheMode(QGraphicsItem.CacheMode.DeviceCoordinateCache)
def set_pos(self, pos):
self.pixmap_item.setPos(pos)
self._pix = pixmap
self.setAcceptHoverEvents(True)
- self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
+ self.setCacheMode(QGraphicsItem.CacheMode.DeviceCoordinateCache)
def boundingRect(self):
return QRectF(-65, -65, 130, 130)
return path
def paint(self, painter, option, widget):
- down = option.state & QStyle.State_Sunken
+ down = option.state & QStyle.StateFlag.State_Sunken
r = self.boundingRect()
grad = QLinearGradient(r.topLeft(), r.bottomRight())
- if option.state & QStyle.State_MouseOver:
- color_0 = Qt.white
+ if option.state & QStyle.StateFlag.State_MouseOver:
+ color_0 = Qt.GlobalColor.white
else:
- color_0 = Qt.lightGray
+ color_0 = Qt.GlobalColor.lightGray
- color_1 = Qt.darkGray
+ color_1 = Qt.GlobalColor.darkGray
if down:
color_0, color_1 = color_1, color_0
grad.setColorAt(0, color_0)
grad.setColorAt(1, color_1)
- painter.setPen(Qt.darkGray)
+ painter.setPen(Qt.GlobalColor.darkGray)
painter.setBrush(grad)
painter.drawEllipse(r)
- color_0 = Qt.darkGray
- color_1 = Qt.lightGray
+ color_0 = Qt.GlobalColor.darkGray
+ color_1 = Qt.GlobalColor.lightGray
if down:
color_0, color_1 = color_1, color_0
grad.setColorAt(0, color_0)
grad.setColorAt(1, color_1)
- painter.setPen(Qt.NoPen)
+ painter.setPen(Qt.PenStyle.NoPen)
painter.setBrush(grad)
if down:
class View(QGraphicsView):
def resizeEvent(self, event):
super(View, self).resizeEvent(event)
- self.fitInView(self.sceneRect(), Qt.KeepAspectRatio)
+ self.fitInView(self.sceneRect(), Qt.AspectRatioMode.KeepAspectRatio)
if __name__ == '__main__':
# Ui.
view = View(scene)
view.setWindowTitle("Animated Tiles")
- view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
+ view.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.BoundingRectViewportUpdate)
view.setBackgroundBrush(QBrush(bg_pix))
- view.setCacheMode(QGraphicsView.CacheBackground)
- view.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
+ view.setCacheMode(QGraphicsView.CacheModeFlag.CacheBackground)
+ view.setRenderHints(QPainter.RenderHint.Antialiasing
+ | QPainter.RenderHint.SmoothPixmapTransform)
view.show()
states = QStateMachine()
for i, item in enumerate(items):
anim = QPropertyAnimation(item, b'pos')
anim.setDuration(750 + i * 25)
- anim.setEasingCurve(QEasingCurve.InOutBack)
+ anim.setEasingCurve(QEasingCurve.Type.InOutBack)
group.addAnimation(anim)
trans = root_state.addTransition(ellipse_button.pressed, ellipse_state)
# Copyright (C) 2010 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
p4.setGeometry(QRectF(0.0, 236.0, 64.0, 64.0))
scene = QGraphicsScene(0, 0, 300, 300)
- scene.setBackgroundBrush(Qt.white)
+ scene.setBackgroundBrush(Qt.GlobalColor.white)
scene.addItem(p1)
scene.addItem(p2)
scene.addItem(p3)
window = QGraphicsView(scene)
window.setFrameStyle(0)
- window.setAlignment(Qt.AlignLeft | Qt.AlignTop)
- window.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
- window.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ window.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
+ window.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
+ window.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
machine = QStateMachine()
- machine.setGlobalRestorePolicy(QStateMachine.RestoreProperties)
+ machine.setGlobalRestorePolicy(QStateMachine.RestorePolicy.RestoreProperties)
group = QState(machine)
selected_rect = QRect(86, 86, 128, 128)
# Copyright (C) 2010 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from enum import IntEnum
import sys
super().__init__()
self.pixmap_item = QGraphicsPixmapItem(pix)
- self.pixmap_item.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
+ self.pixmap_item.setCacheMode(QGraphicsItem.CacheMode.DeviceCoordinateCache)
def set_pos(self, pos):
self.pixmap_item.setPos(pos)
self._ui.graphicsView.setScene(self._scene)
self._anim = Animation(self._item, b'pos')
- self._anim.setEasingCurve(QEasingCurve.OutBounce)
+ self._anim.setEasingCurve(QEasingCurve.Type.OutBounce)
self._ui.easingCurvePicker.setCurrentRow(0)
self.start_animation()
curve_scale = self._iconSize.height() / 2.0
- painter.setPen(Qt.NoPen)
+ painter.setPen(Qt.PenStyle.NoPen)
# Start point.
- painter.setBrush(Qt.red)
+ painter.setBrush(Qt.GlobalColor.red)
start = QPoint(y_axis, x_axis - curve_scale * curve.valueForProgress(0))
painter.drawRect(start.x() - 1, start.y() - 1, 3, 3)
# End point.
- painter.setBrush(Qt.blue)
+ painter.setBrush(Qt.GlobalColor.blue)
end = QPoint(y_axis + curve_scale,
x_axis - curve_scale * curve.valueForProgress(1))
painter.drawRect(end.x() - 1, end.y() - 1, 3, 3)
curve_path.lineTo(to)
t += 1.0 / curve_scale
- painter.setRenderHint(QPainter.Antialiasing, True)
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
painter.strokePath(curve_path, QColor(32, 32, 32))
- painter.setRenderHint(QPainter.Antialiasing, False)
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing, False)
item = QListWidgetItem()
item.setIcon(QIcon(pix))
self._anim.setEasingCurve(curve_type)
self._anim.setCurrentTime(0)
- is_elastic = (curve_type.value >= QEasingCurve.InElastic.value
- and curve_type.value <= QEasingCurve.OutInElastic.value)
- is_bounce = (curve_type.value >= QEasingCurve.InBounce.value
- and curve_type.value <= QEasingCurve.OutInBounce.value)
+ is_elastic = (curve_type.value >= QEasingCurve.Type.InElastic.value
+ and curve_type.value <= QEasingCurve.Type.OutInElastic.value)
+ is_bounce = (curve_type.value >= QEasingCurve.Type.InBounce.value
+ and curve_type.value <= QEasingCurve.Type.OutInBounce.value)
self._ui.periodSpinBox.setEnabled(is_elastic)
self._ui.amplitudeSpinBox.setEnabled(is_elastic or is_bounce)
- self._ui.overshootSpinBox.setEnabled(curve_type.value >= QEasingCurve.InBack.value
- and curve_type.value <= QEasingCurve.OutInBack.value)
+ overshoot = (curve_type.value >= QEasingCurve.Type.InBack.value
+ and curve_type.value <= QEasingCurve.Type.OutInBack.value)
+ self._ui.overshootSpinBox.setEnabled(overshoot)
def path_changed(self, index):
self._anim.set_path_type(index)
</size>
</property>
<property name="verticalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOff</enum>
+ <enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
</property>
<property name="movement">
- <enum>QListView::Static</enum>
+ <enum>QListView::Movement::Static</enum>
</property>
<property name="isWrapping" stdset="0">
<bool>false</bool>
</property>
<property name="viewMode">
- <enum>QListView::IconMode</enum>
+ <enum>QListView::ViewMode::IconMode</enum>
</property>
<property name="selectionRectVisible">
<bool>false</bool>
</size>
</property>
<property name="layoutDirection">
- <enum>Qt::LeftToRight</enum>
+ <enum>Qt::LayoutDirection::LeftToRight</enum>
</property>
<property name="text">
<string>Line</string>
<bool>true</bool>
</property>
<attribute name="buttonGroup">
- <string>buttonGroup</string>
+ <string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<string>Circle</string>
</property>
<attribute name="buttonGroup">
- <string>buttonGroup</string>
+ <string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
- <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
+ <enum>QFormLayout::FieldGrowthPolicy::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
- <enum>Qt::Vertical</enum>
+ <enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
################################################################################
## Form generated from reading UI file 'form.ui'
##
-## Created by: Qt User Interface Compiler version 6.7.0
+## Created by: Qt User Interface Compiler version 6.8.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
sizePolicy.setHeightForWidth(self.easingCurvePicker.sizePolicy().hasHeightForWidth())
self.easingCurvePicker.setSizePolicy(sizePolicy)
self.easingCurvePicker.setMaximumSize(QSize(16777215, 120))
- self.easingCurvePicker.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
- self.easingCurvePicker.setMovement(QListView.Static)
- self.easingCurvePicker.setProperty("isWrapping", False)
- self.easingCurvePicker.setViewMode(QListView.IconMode)
+ self.easingCurvePicker.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
+ self.easingCurvePicker.setMovement(QListView.Movement.Static)
+ self.easingCurvePicker.setProperty(u"isWrapping", False)
+ self.easingCurvePicker.setViewMode(QListView.ViewMode.IconMode)
self.easingCurvePicker.setSelectionRectVisible(False)
self.gridLayout.addWidget(self.easingCurvePicker, 0, 0, 1, 2)
self.buttonGroup.addButton(self.lineRadio)
self.lineRadio.setObjectName(u"lineRadio")
self.lineRadio.setMaximumSize(QSize(16777215, 40))
- self.lineRadio.setLayoutDirection(Qt.LeftToRight)
+ self.lineRadio.setLayoutDirection(Qt.LayoutDirection.LeftToRight)
self.lineRadio.setChecked(True)
self.gridLayout_2.addWidget(self.lineRadio, 0, 0, 1, 1)
self.groupBox.setSizePolicy(sizePolicy1)
self.formLayout = QFormLayout(self.groupBox)
self.formLayout.setObjectName(u"formLayout")
- self.formLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
+ self.formLayout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
self.label = QLabel(self.groupBox)
self.label.setObjectName(u"label")
sizePolicy2 = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
self.label.setSizePolicy(sizePolicy2)
self.label.setMinimumSize(QSize(0, 30))
- self.formLayout.setWidget(0, QFormLayout.LabelRole, self.label)
+ self.formLayout.setWidget(0, QFormLayout.ItemRole.LabelRole, self.label)
self.periodSpinBox = QDoubleSpinBox(self.groupBox)
self.periodSpinBox.setObjectName(u"periodSpinBox")
self.periodSpinBox.setSingleStep(0.100000000000000)
self.periodSpinBox.setValue(-1.000000000000000)
- self.formLayout.setWidget(0, QFormLayout.FieldRole, self.periodSpinBox)
+ self.formLayout.setWidget(0, QFormLayout.ItemRole.FieldRole, self.periodSpinBox)
self.amplitudeSpinBox = QDoubleSpinBox(self.groupBox)
self.amplitudeSpinBox.setObjectName(u"amplitudeSpinBox")
self.amplitudeSpinBox.setSingleStep(0.100000000000000)
self.amplitudeSpinBox.setValue(-1.000000000000000)
- self.formLayout.setWidget(2, QFormLayout.FieldRole, self.amplitudeSpinBox)
+ self.formLayout.setWidget(2, QFormLayout.ItemRole.FieldRole, self.amplitudeSpinBox)
self.label_3 = QLabel(self.groupBox)
self.label_3.setObjectName(u"label_3")
self.label_3.setMinimumSize(QSize(0, 30))
- self.formLayout.setWidget(4, QFormLayout.LabelRole, self.label_3)
+ self.formLayout.setWidget(4, QFormLayout.ItemRole.LabelRole, self.label_3)
self.overshootSpinBox = QDoubleSpinBox(self.groupBox)
self.overshootSpinBox.setObjectName(u"overshootSpinBox")
self.overshootSpinBox.setSingleStep(0.100000000000000)
self.overshootSpinBox.setValue(-1.000000000000000)
- self.formLayout.setWidget(4, QFormLayout.FieldRole, self.overshootSpinBox)
+ self.formLayout.setWidget(4, QFormLayout.ItemRole.FieldRole, self.overshootSpinBox)
self.label_2 = QLabel(self.groupBox)
self.label_2.setObjectName(u"label_2")
self.label_2.setMinimumSize(QSize(0, 30))
- self.formLayout.setWidget(2, QFormLayout.LabelRole, self.label_2)
+ self.formLayout.setWidget(2, QFormLayout.ItemRole.LabelRole, self.label_2)
self.verticalLayout.addWidget(self.groupBox)
# Copyright (C) 2010 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Parent widget.
widget = QGraphicsWidget()
- layout = QGraphicsLinearLayout(Qt.Vertical, widget)
+ layout = QGraphicsLinearLayout(Qt.Orientation.Vertical, widget)
layout.addItem(edit_proxy)
layout.addItem(button_proxy)
widget.setLayout(layout)
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
# Resource object code
#
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/desktop/screenshot example from Qt v6.x"""
self.screenshot_label = QLabel(self)
- self.screenshot_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
- self.screenshot_label.setAlignment(Qt.AlignCenter)
+ self.screenshot_label.setSizePolicy(QSizePolicy.Policy.Expanding,
+ QSizePolicy.Policy.Expanding)
+ self.screenshot_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
screen_geometry: QRect = self.screen().geometry()
self.screenshot_label.setMinimumSize(
save_screenshot_button.clicked.connect(self.save_screenshot)
buttons_layout.addWidget(save_screenshot_button)
quit_screenshot_button = QPushButton("Quit", self)
- quit_screenshot_button.setShortcut(Qt.CTRL | Qt.Key_Q)
+ quit_screenshot_button.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_Q)
quit_screenshot_button.clicked.connect(self.close)
buttons_layout.addWidget(quit_screenshot_button)
buttons_layout.addStretch()
def resizeEvent(self, event):
scaled_size = self.original_pixmap.size()
- scaled_size.scale(self.screenshot_label.size(), Qt.KeepAspectRatio)
+ scaled_size.scale(self.screenshot_label.size(), Qt.AspectRatioMode.KeepAspectRatio)
if scaled_size != self.screenshot_label.pixmap().size():
self.update_screenshot_label()
@Slot()
def save_screenshot(self):
fmt = "png" # In order to avoid shadowing built-in format
- initial_path = QStandardPaths.writableLocation(QStandardPaths.PicturesLocation)
+ initial_path = QStandardPaths.writableLocation(QStandardPaths.StandardLocation.PicturesLocation) # noqa: E501
if not initial_path:
initial_path = QDir.currentPath()
initial_path += f"/untitled.{fmt}"
fileDialog = QFileDialog(self, "Save As", initial_path)
- fileDialog.setAcceptMode(QFileDialog.AcceptSave)
- fileDialog.setFileMode(QFileDialog.AnyFile)
+ fileDialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
+ fileDialog.setFileMode(QFileDialog.FileMode.AnyFile)
fileDialog.setDirectory(initial_path)
mime_types = []
fileDialog.setMimeTypeFilters(mime_types)
fileDialog.selectMimeTypeFilter("image/" + fmt)
fileDialog.setDefaultSuffix(fmt)
- if fileDialog.exec() != QDialog.Accepted:
+ if fileDialog.exec() != QDialog.DialogCode.Accepted:
return
file_name = fileDialog.selectedFiles()[0]
self.screenshot_label.setPixmap(
self.original_pixmap.scaled(
self.screenshot_label.size(),
- Qt.KeepAspectRatio,
- Qt.SmoothTransformation,
+ Qt.AspectRatioMode.KeepAspectRatio,
+ Qt.TransformationMode.SmoothTransformation,
)
)
--- /dev/null
+System Tray Icon Example
+========================
+
+The System Tray Icon example shows how to add an icon with a menu and popup
+messages to a desktop environment's system tray. It demonstrates the analogous
+Qt example `System Tray Icon Example
+<https://doc.qt.io/qt-6/qtwidgets-desktop-systray-example.html>`_.
+
+.. image:: systray.png
+ :align: center
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Slot
from PySide6.QtGui import QAction, QIcon
self._type_label = QLabel("Type:")
self._type_combo_box = QComboBox()
- self._type_combo_box.addItem("None", QSystemTrayIcon.NoIcon)
+ self._type_combo_box.addItem("None", QSystemTrayIcon.MessageIcon.NoIcon)
self._type_combo_box.addItem(
- self.style().standardIcon(QStyle.SP_MessageBoxInformation),
+ self.style().standardIcon(QStyle.StandardPixmap.SP_MessageBoxInformation),
"Information",
- QSystemTrayIcon.Information,
+ QSystemTrayIcon.MessageIcon.Information,
)
self._type_combo_box.addItem(
- self.style().standardIcon(QStyle.SP_MessageBoxWarning),
+ self.style().standardIcon(QStyle.StandardPixmap.SP_MessageBoxWarning),
"Warning",
- QSystemTrayIcon.Warning,
+ QSystemTrayIcon.MessageIcon.Warning,
)
self._type_combo_box.addItem(
- self.style().standardIcon(QStyle.SP_MessageBoxCritical),
+ self.style().standardIcon(QStyle.StandardPixmap.SP_MessageBoxCritical),
"Critical",
- QSystemTrayIcon.Critical,
+ QSystemTrayIcon.MessageIcon.Critical,
)
self._type_combo_box.addItem(QIcon(), "Custom icon", -1)
self._type_combo_box.setCurrentIndex(1)
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import os
from pathlib import Path
self._output_index = self.addPage(OutputFilesPage())
self.addPage(ConclusionPage())
- self.setPixmap(QWizard.BannerPixmap,
+ self.setPixmap(QWizard.WizardPixmap.BannerPixmap,
QPixmap(':/images/banner.png'))
- self.setPixmap(QWizard.BackgroundPixmap,
+ self.setPixmap(QWizard.WizardPixmap.BackgroundPixmap,
QPixmap(':/images/background.png'))
self.setWindowTitle("Class Wizard")
super().__init__(parent)
self.setTitle("Introduction")
- self.setPixmap(QWizard.WatermarkPixmap,
+ self.setPixmap(QWizard.WizardPixmap.WatermarkPixmap,
QPixmap(':/images/watermark1.png'))
label = QLabel(INTRODUCTION)
self.setTitle("Class Information")
self.setSubTitle("Specify basic information about the class for "
"which you want to generate a skeleton source code file.")
- self.setPixmap(QWizard.LogoPixmap,
+ self.setPixmap(QWizard.WizardPixmap.LogoPixmap,
QPixmap(':/qt-project.org/logos/pysidelogo.png'))
class_name_line_edit = QLineEdit()
self.setTitle("QObject parameters")
self.setSubTitle("Specify the signals, slots and properties.")
- self.setPixmap(QWizard.LogoPixmap,
+ self.setPixmap(QWizard.WizardPixmap.LogoPixmap,
QPixmap(':/qt-project.org/logos/pysidelogo.png'))
layout = QVBoxLayout(self)
self._properties_chooser = PropertyChooser()
self.setTitle("Output Files")
self.setSubTitle("Specify where you want the wizard to put the "
"generated skeleton code.")
- self.setPixmap(QWizard.LogoPixmap,
+ self.setPixmap(QWizard.WizardPixmap.LogoPixmap,
QPixmap(':/qt-project.org/logos/pysidelogo.png'))
output_dir_label = QLabel("&Output directory:")
super().__init__(parent)
self.setTitle("Conclusion")
- self.setPixmap(QWizard.WatermarkPixmap,
+ self.setPixmap(QWizard.WizardPixmap.WatermarkPixmap,
QPixmap(':/images/watermark1.png'))
self.label = QLabel()
layout.addWidget(self._launch_check_box)
def initializePage(self):
- finish_text = self.wizard().buttonText(QWizard.FinishButton)
+ finish_text = self.wizard().buttonText(QWizard.WizardButton.FinishButton)
finish_text = finish_text.replace('&', '')
self.label.setText(f"Click {finish_text} to generate the class skeleton.")
self._launch_check_box.setChecked(True)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QRegularExpression, Property, Slot
from PySide6.QtGui import QRegularExpressionValidator
self._form_layout.addRow(label, self._lineedit)
layout.addLayout(self._form_layout)
- bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ bb = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok
+ | QDialogButtonBox.StandardButton.Cancel)
layout.addWidget(bb)
bb.rejected.connect(self.reject)
bb.accepted.connect(self.accept)
--- /dev/null
+Extension Example
+=================
+
+This example demonstrates a PySide6 application that creates a find dialog with
+an extendable interface, showcasing how to add and manage additional options
+dynamically within a dialog. It demonstrates the analogous Qt example
+`Extension Example <https://doc.qt.io/qt-6.2/qtwidgets-dialogs-extension-example.html>`_.
+
+.. image:: extension.png
+ :width: 400
+ :alt: extension screenshot
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/dialogs/extension example from Qt v5.x"""
more_button.setCheckable(True)
more_button.setAutoDefault(False)
- button_box = QDialogButtonBox(Qt.Vertical)
- button_box.addButton(find_button, QDialogButtonBox.ActionRole)
- button_box.addButton(more_button, QDialogButtonBox.ActionRole)
+ button_box = QDialogButtonBox(Qt.Orientation.Vertical)
+ button_box.addButton(find_button, QDialogButtonBox.ButtonRole.ActionRole)
+ button_box.addButton(more_button, QDialogButtonBox.ButtonRole.ActionRole)
extension = QWidget()
left_layout.addStretch(1)
main_layout = QGridLayout(self)
- main_layout.setSizeConstraint(QLayout.SetFixedSize)
+ main_layout.setSizeConstraint(QLayout.SizeConstraint.SetFixedSize)
main_layout.addLayout(left_layout, 0, 0)
main_layout.addWidget(button_box, 0, 1)
main_layout.addWidget(extension, 1, 0, 1, 2)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from enum import IntEnum
path = Path(__file__).resolve().parent
- self.setPixmap(QWizard.WatermarkPixmap, QPixmap(path / "images" / "watermark.png"))
+ self.setPixmap(QWizard.WizardPixmap.WatermarkPixmap,
+ QPixmap(path / "images" / "watermark.png"))
self.top_label = QLabel(
"This wizard will help you register your copy of "
"<i>Super Product One</i>™ or start "
self.setTitle("Complete Your Registration")
path = Path(__file__).resolve().parent
- self.setPixmap(QWizard.WatermarkPixmap, QPixmap(path / "images" / "watermark.png"))
+ self.setPixmap(QWizard.WizardPixmap.WatermarkPixmap,
+ QPixmap(path / "images" / "watermark.png"))
self.bottom_label = QLabel()
self.bottom_label.setWordWrap(True)
def setVisible(self, visible: bool):
super().setVisible(visible)
if visible:
- self.wizard().setButtonText(QWizard.CustomButton1, "&Print")
- self.wizard().setOption(QWizard.HaveCustomButton1, True)
+ self.wizard().setButtonText(QWizard.WizardButton.CustomButton1, "&Print")
+ self.wizard().setOption(QWizard.WizardOption.HaveCustomButton1, True)
if not self.custom_button_clicked_signal_connected:
self.custom_button_clicked_signal_connected = True
self.wizard().customButtonClicked.connect(self.print_button_clicked)
else:
- self.wizard().setOption(QWizard.HaveCustomButton1, False)
+ self.wizard().setOption(QWizard.WizardOption.HaveCustomButton1, False)
if self.custom_button_clicked_signal_connected:
self.custom_button_clicked_signal_connected = False
self.setStartId(Pages.Page_Intro)
if sys.platform == 'darwin':
- self.setWizardStyle(QWizard.ModernStyle)
+ self.setWizardStyle(QWizard.WizardStyle.ModernStyle)
- self.setOption(QWizard.HaveHelpButton, True)
+ self.setOption(QWizard.WizardOption.HaveHelpButton, True)
path = Path(__file__).resolve().parent
- self.setPixmap(QWizard.LogoPixmap, QPixmap(path / "images" / "logo.png"))
+ self.setPixmap(QWizard.WizardPixmap.LogoPixmap, QPixmap(path / "images" / "logo.png"))
self.helpRequested.connect(self.show_help)
self.setWindowTitle("License Wizard")
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
--- /dev/null
+Standard Dialogs Example
+========================
+
+The Standard Dialogs example shows the standard dialogs that are provided by
+Qt.
+
+.. image:: standarddialogs.png
+ :align: center
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/dialogs/standarddialogs example from Qt v5.x"""
class DialogOptionsWidget(QGroupBox):
"""Widget displaying a number of check boxes representing the dialog
options."""
- def __init__(self, parent=None):
+ def __init__(self, zero_value, parent=None):
super().__init__(parent)
+ self._zero_value = zero_value
self._layout = QVBoxLayout(self)
self._mapping = {}
def value(self):
- result = 0
+ result = self._zero_value
for checkbox, value in self._mapping.items():
if checkbox.isChecked():
result |= value
self._error_message_dialog = QErrorMessage(self)
- frame_style = QFrame.Sunken | QFrame.Panel
+ frame_style = QFrame.Shadow.Sunken | QFrame.Shape.Panel
self._integer_label = QLabel()
self._integer_label.setFrameStyle(frame_style)
self._color_label = QLabel()
self._color_label.setFrameStyle(frame_style)
self._color_button = QPushButton("QColorDialog.get&Color()")
- self._color_options = DialogOptionsWidget()
+ self._color_options = DialogOptionsWidget(QColorDialog.ColorDialogOption(0))
self._color_options.add_checkbox("Show alpha channel",
- QColorDialog.ShowAlphaChannel)
+ QColorDialog.ColorDialogOption.ShowAlphaChannel)
self._color_options.add_checkbox("No buttons",
- QColorDialog.NoButtons)
+ QColorDialog.ColorDialogOption.NoButtons)
self._font_label = QLabel()
self._font_label.setFrameStyle(frame_style)
self._font_button = QPushButton("QFontDialog.get&Font()")
- self._font_options = DialogOptionsWidget()
+ self._font_options = DialogOptionsWidget(QFontDialog.FontDialogOption(0))
self._font_options.add_checkbox("Do not use native dialog",
- QFontDialog.DontUseNativeDialog)
+ QFontDialog.FontDialogOption.DontUseNativeDialog)
self._font_options.add_checkbox("Show scalable fonts",
- QFontDialog.ScalableFonts)
+ QFontDialog.FontDialogOption.ScalableFonts)
self._font_options.add_checkbox("Show non-scalable fonts",
- QFontDialog.NonScalableFonts)
+ QFontDialog.FontDialogOption.NonScalableFonts)
self._font_options.add_checkbox("Show monospaced fonts",
- QFontDialog.MonospacedFonts)
+ QFontDialog.FontDialogOption.MonospacedFonts)
self._font_options.add_checkbox("Show proportional fonts",
- QFontDialog.ProportionalFonts)
- self._font_options.add_checkbox("No buttons", QFontDialog.NoButtons)
+ QFontDialog.FontDialogOption.ProportionalFonts)
+ self._font_options.add_checkbox("No buttons", QFontDialog.FontDialogOption.NoButtons)
self._directory_label = QLabel()
self._directory_label.setFrameStyle(frame_style)
self._save_file_name_label.setFrameStyle(frame_style)
self._save_file_name_button = QPushButton("QFileDialog.get&SaveFileName()")
- self._file_options = DialogOptionsWidget()
+ self._file_options = DialogOptionsWidget(QFileDialog.Option(0))
self._file_options.add_checkbox("Do not use native dialog",
- QFileDialog.DontUseNativeDialog)
+ QFileDialog.Option.DontUseNativeDialog)
self._file_options.add_checkbox("Show directories only",
- QFileDialog.ShowDirsOnly)
+ QFileDialog.Option.ShowDirsOnly)
self._file_options.add_checkbox("Do not resolve symlinks",
- QFileDialog.DontResolveSymlinks)
+ QFileDialog.Option.DontResolveSymlinks)
self._file_options.add_checkbox("Do not confirm overwrite",
- QFileDialog.DontConfirmOverwrite)
- self._file_options.add_checkbox("Readonly", QFileDialog.ReadOnly)
+ QFileDialog.Option.DontConfirmOverwrite)
+ self._file_options.add_checkbox("Readonly", QFileDialog.Option.ReadOnly)
self._file_options.add_checkbox("Hide name filter details",
- QFileDialog.HideNameFilterDetails)
+ QFileDialog.Option.HideNameFilterDetails)
self._file_options.add_checkbox("Do not use custom directory icons (Windows)",
- QFileDialog.DontUseCustomDirectoryIcons)
+ QFileDialog.Option.DontUseCustomDirectoryIcons)
self._critical_label = QLabel()
self._critical_label.setFrameStyle(frame_style)
layout.addWidget(self._text_label, 3, 1)
layout.addWidget(self._multiline_text_label, 4, 1)
layout.addWidget(self._multiline_text_button, 4, 0)
- spacer = QSpacerItem(0, 0, QSizePolicy.Ignored, QSizePolicy.MinimumExpanding)
+ spacer = QSpacerItem(0, 0, QSizePolicy.Policy.Ignored, QSizePolicy.Policy.MinimumExpanding)
layout.addItem(spacer, 5, 0)
toolbox.addItem(page, "Input Dialogs")
layout = QGridLayout(page)
layout.addWidget(self._color_button, 0, 0)
layout.addWidget(self._color_label, 0, 1)
- spacer = QSpacerItem(0, 0, QSizePolicy.Ignored, QSizePolicy.MinimumExpanding)
+ spacer = QSpacerItem(0, 0, QSizePolicy.Policy.Ignored, QSizePolicy.Policy.MinimumExpanding)
layout.addItem(spacer, 1, 0)
layout.addWidget(self._color_options, 2, 0, 1, 2)
toolbox.addItem(page, "Color Dialog")
layout = QGridLayout(page)
layout.addWidget(self._font_button, 0, 0)
layout.addWidget(self._font_label, 0, 1)
- spacer = QSpacerItem(0, 0, QSizePolicy.Ignored, QSizePolicy.MinimumExpanding)
+ spacer = QSpacerItem(0, 0, QSizePolicy.Policy.Ignored, QSizePolicy.Policy.MinimumExpanding)
layout.addItem(spacer, 1, 0)
layout.addWidget(self._font_options, 2, 0, 1, 2)
toolbox.addItem(page, "Font Dialog")
layout.addWidget(self._open_file_names_label, 2, 1)
layout.addWidget(self._save_file_name_button, 3, 0)
layout.addWidget(self._save_file_name_label, 3, 1)
- spacer = QSpacerItem(0, 0, QSizePolicy.Ignored, QSizePolicy.MinimumExpanding)
+ spacer = QSpacerItem(0, 0, QSizePolicy.Policy.Ignored, QSizePolicy.Policy.MinimumExpanding)
layout.addItem(spacer, 4, 0)
layout.addWidget(self._file_options, 5, 0, 1, 2)
layout.addWidget(self._warning_button, 3, 0)
layout.addWidget(self._warning_label, 3, 1)
layout.addWidget(self._error_button, 4, 0)
- spacer = QSpacerItem(0, 0, QSizePolicy.Ignored, QSizePolicy.MinimumExpanding)
+ spacer = QSpacerItem(0, 0, QSizePolicy.Policy.Ignored, QSizePolicy.Policy.MinimumExpanding)
layout.addItem(spacer, 5, 0)
toolbox.addItem(page, "Message Boxes")
@Slot()
def set_color(self):
options_value = self._color_options.value()
- options = QColorDialog.ColorDialogOptions(options_value)
- color = QColorDialog.getColor(Qt.green, self, "Select Color", options)
+ options = QColorDialog.ColorDialogOption(options_value)
+ color = QColorDialog.getColor(Qt.GlobalColor.green, self, "Select Color", options)
if color.isValid():
self._color_label.setText(color.name())
Activating the liquid oxygen stirring fans caused an
explosion in one of the tanks. Liquid oxygen levels
are getting low. This may jeopardize the moon landing mission.""")
- msg_box = QMessageBox(QMessageBox.Critical, "QMessageBox.critical()",
+ msg_box = QMessageBox(QMessageBox.Icon.Critical, "QMessageBox.critical()",
"Houston, we have a problem",
- QMessageBox.Abort | QMessageBox.Retry | QMessageBox.Ignore,
- self)
+ QMessageBox.StandardButton.Abort | QMessageBox.StandardButton.Retry
+ | QMessageBox.StandardButton.Ignore, self)
msg_box.setInformativeText(m)
reply = msg_box.exec()
- if reply == QMessageBox.Abort:
+ if reply == QMessageBox.StandardButton.Abort:
self._critical_label.setText("Abort")
- elif reply == QMessageBox.Retry:
+ elif reply == QMessageBox.StandardButton.Retry:
self._critical_label.setText("Retry")
else:
self._critical_label.setText("Ignore")
the conclusion of Elvis Presley concerts in order to
disperse audiences who lingered in hopes of an encore.
It has since become a catchphrase and punchline.""")
- msg_box = QMessageBox(QMessageBox.Information, "QMessageBox.information()",
+ msg_box = QMessageBox(QMessageBox.Icon.Information, "QMessageBox.information()",
"Elvis has left the building.",
- QMessageBox.Ok, self)
+ QMessageBox.StandardButton.Ok, self)
msg_box.setInformativeText(m)
reply = msg_box.exec()
- if reply == QMessageBox.Ok:
+ if reply == QMessageBox.StandardButton.Ok:
self._information_label.setText("OK")
else:
self._information_label.setText("Escape")
meat patty. The cheese is usually added to the cooking
hamburger patty shortly before serving, which allows the
cheese to melt.""")
- msg_box = QMessageBox(QMessageBox.Question, "QMessageBox.question()",
+ msg_box = QMessageBox(QMessageBox.Icon.Question, "QMessageBox.question()",
"Would you like cheese with that?",
- QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
+ | QMessageBox.StandardButton.Cancel)
msg_box.setInformativeText(m)
reply = msg_box.exec()
- if reply == QMessageBox.Yes:
+ if reply == QMessageBox.StandardButton.Yes:
self._question_label.setText("Yes")
- elif reply == QMessageBox.No:
+ elif reply == QMessageBox.StandardButton.No:
self._question_label.setText("No")
else:
self._question_label.setText("Cancel")
@Slot()
def warning_message(self):
- msg_box = QMessageBox(QMessageBox.Warning, "QMessageBox.warning()",
+ msg_box = QMessageBox(QMessageBox.Icon.Warning, "QMessageBox.warning()",
"Delete the only copy of your movie manuscript?",
- QMessageBox.NoButton, self)
+ QMessageBox.StandardButton.NoButton, self)
m = "You've been working on this manuscript for 738 days now. Hang in there!"
msg_box.setInformativeText(m)
msg_box.setDetailedText('"A long time ago in a galaxy far, far away...."')
- msg_box.addButton("&Keep", QMessageBox.AcceptRole)
- msg_box.addButton("Delete", QMessageBox.RejectRole)
- if msg_box.exec() == QMessageBox.AcceptRole:
+ msg_box.addButton("&Keep", QMessageBox.ButtonRole.AcceptRole)
+ msg_box.addButton("Delete", QMessageBox.ButtonRole.RejectRole)
+ if msg_box.exec() == QMessageBox.ButtonRole.AcceptRole:
self._warning_label.setText("Keep")
else:
self._warning_label.setText("Delete")
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/dialogs/tabdialog example from Qt v6.x"""
tab_widget.addTab(ApplicationsTab(file_info, self), "Applications")
button_box = QDialogButtonBox(
- QDialogButtonBox.Ok | QDialogButtonBox.Cancel
+ QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
)
button_box.accepted.connect(self.accept)
path_label = QLabel("Path:")
path_value_label = QLabel(file_info.absoluteFilePath())
- path_value_label.setFrameStyle(QFrame.Panel | QFrame.Sunken)
+ path_value_label.setFrameStyle(QFrame.Shape.Panel | QFrame.Shadow.Sunken)
size_label = QLabel("Size:")
size = file_info.size() / 1024
size_value_label = QLabel(f"{size} K")
- size_value_label.setFrameStyle(QFrame.Panel | QFrame.Sunken)
+ size_value_label.setFrameStyle(QFrame.Shape.Panel | QFrame.Shadow.Sunken)
last_read_label = QLabel("Last Read:")
last_read_value_label = QLabel(file_info.lastRead().toString())
- last_read_value_label.setFrameStyle(QFrame.Panel | QFrame.Sunken)
+ last_read_value_label.setFrameStyle(QFrame.Shape.Panel | QFrame.Shadow.Sunken)
last_mod_label = QLabel("Last Modified:")
last_mod_value_label = QLabel(file_info.lastModified().toString())
- last_mod_value_label.setFrameStyle(QFrame.Panel | QFrame.Sunken)
+ last_mod_value_label.setFrameStyle(QFrame.Shape.Panel | QFrame.Shadow.Sunken)
main_layout = QVBoxLayout()
main_layout.addWidget(file_name_label)
owner_label = QLabel("Owner")
owner_value_label = QLabel(file_info.owner())
- owner_value_label.setFrameStyle(QFrame.Panel | QFrame.Sunken)
+ owner_value_label.setFrameStyle(QFrame.Shape.Panel | QFrame.Shadow.Sunken)
group_label = QLabel("Group")
group_value_label = QLabel(file_info.group())
- group_value_label.setFrameStyle(QFrame.Panel | QFrame.Sunken)
+ group_value_label.setFrameStyle(QFrame.Shape.Panel | QFrame.Shadow.Sunken)
permissions_layout = QVBoxLayout()
permissions_layout.addWidget(readable)
--- /dev/null
+Trivial Wizard Example
+======================
+
+The Trivial Wizard example illustrates how to create a linear three-page
+registration wizard using three instances of `QWizardPage` and one instance of
+`QWizard`. This example demonstrates the analogous Qt example `Trivial Wizard
+Example <https://doc.qt.io/qt-6/qtwidgets-dialogs-trivialwizard-example.html>`_.
+
+.. image:: trivialwizard.png
+ :width: 400
+ :alt: trivialwizard screenshot
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/dialogs/trivialwizard example from Qt v5.x"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from pathlib import Path
import sys
def __init__(self, parent: QWidget):
super().__init__(parent)
self.setMinimumSize(200, 200)
- self.setFrameStyle(QFrame.Sunken | QFrame.StyledPanel)
+ self.setFrameStyle(QFrame.Shadow.Sunken | QFrame.Shape.StyledPanel)
self.setAcceptDrops(True)
path = Path(__file__).resolve().parent
boat_icon.setPixmap(QPixmap(path / "images" / "boat.png"))
boat_icon.move(10, 10)
boat_icon.show()
- boat_icon.setAttribute(Qt.WA_DeleteOnClose)
+ boat_icon.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
car_icon = QLabel(self)
car_icon.setPixmap(QPixmap(path / "images" / "car.png"))
car_icon.move(100, 10)
car_icon.show()
- car_icon.setAttribute(Qt.WA_DeleteOnClose)
+ car_icon.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
house_icon = QLabel(self)
house_icon.setPixmap(QPixmap(path / "images" / "house.png"))
house_icon.move(10, 80)
house_icon.show()
- house_icon.setAttribute(Qt.WA_DeleteOnClose)
+ house_icon.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
def dragEnterEvent(self, event):
if event.mimeData().hasFormat("application/x-dnditem_data"):
if event.source() == self:
- event.setDropAction(Qt.MoveAction)
+ event.setDropAction(Qt.DropAction.MoveAction)
event.accept()
else:
event.acceptProposedAction()
def dragMoveEvent(self, event):
if event.mimeData().hasFormat("application/x-dnditem_data"):
if event.source() == self:
- event.setDropAction(Qt.MoveAction)
+ event.setDropAction(Qt.DropAction.MoveAction)
event.accept()
else:
event.acceptProposedAction()
def dropEvent(self, event):
if event.mimeData().hasFormat("application/x-dnditem_data"):
item_data: QByteArray = event.mimeData().data("application/x-dnditem_data")
- data_stream = QDataStream(item_data, QIODevice.ReadOnly)
+ data_stream = QDataStream(item_data, QIODevice.OpenModeFlag.ReadOnly)
pixmap = QPixmap()
offset = QPoint()
new_icon.setPixmap(pixmap)
new_icon.move(event.position().toPoint() - offset)
new_icon.show()
- new_icon.setAttribute(Qt.WA_DeleteOnClose)
+ new_icon.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
if event.source() == self:
- event.setDropAction(Qt.MoveAction)
+ event.setDropAction(Qt.DropAction.MoveAction)
event.accept()
else:
event.acceptProposedAction()
pixmap = child.pixmap()
item_data = QByteArray()
- data_stream = QDataStream(item_data, QIODevice.WriteOnly)
+ data_stream = QDataStream(item_data, QIODevice.OpenModeFlag.WriteOnly)
data_stream << pixmap << QPoint(event.position().toPoint() - child.pos())
child.setPixmap(temp_pixmap)
- if drag.exec(Qt.CopyAction | Qt.MoveAction, Qt.CopyAction) == Qt.MoveAction:
+ if drag.exec(Qt.DropAction.CopyAction | Qt.DropAction.MoveAction,
+ Qt.DropAction.CopyAction) == Qt.DropAction.MoveAction:
child.close()
else:
child.show()
--- /dev/null
+Draggable Text Example
+======================
+
+Illustrates how to drag and drop text between widgets.
+
+.. image:: draggabletext.png
+ :width: 400
+ :alt: draggabletext screenshot
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/draganddrop/draggabletext example from Qt v5.x,
originating from PyQt"""
super().__init__(text, parent)
self.setAutoFillBackground(True)
- self.setFrameShape(QFrame.Panel)
- self.setFrameShadow(QFrame.Raised)
+ self.setFrameShape(QFrame.Shape.Panel)
+ self.setFrameShadow(QFrame.Shadow.Raised)
def mousePressEvent(self, event):
hot_spot = event.position().toPoint()
drag.setPixmap(pixmap)
drag.setHotSpot(hot_spot)
- drop_action = drag.exec(Qt.CopyAction | Qt.MoveAction, Qt.CopyAction)
+ drop_action = drag.exec(Qt.DropAction.CopyAction | Qt.DropAction.MoveAction,
+ Qt.DropAction.CopyAction)
- if drop_action == Qt.MoveAction:
+ if drop_action == Qt.DropAction.MoveAction:
self.close()
self.update()
super().__init__(parent)
dictionary_file = QFile(':/dictionary/words.txt')
- dictionary_file.open(QIODevice.ReadOnly)
+ dictionary_file.open(QIODevice.OpenModeFlag.ReadOnly)
x = 5
y = 5
y += word_label.height() + 2
new_palette = self.palette()
- new_palette.setColor(QPalette.Window, Qt.white)
+ new_palette.setColor(QPalette.ColorRole.Window, Qt.GlobalColor.white)
self.setPalette(new_palette)
self.setAcceptDrops(True)
def dragEnterEvent(self, event):
if event.mimeData().hasText():
if event.source() in self.children():
- event.setDropAction(Qt.MoveAction)
+ event.setDropAction(Qt.DropAction.MoveAction)
event.accept()
else:
event.acceptProposedAction()
position += QPoint(new_label.width(), 0)
if event.source() in self.children():
- event.setDropAction(Qt.MoveAction)
+ event.setDropAction(Qt.DropAction.MoveAction)
event.accept()
else:
event.acceptProposedAction()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QMimeData, Qt, Slot, Signal
from PySide6.QtGui import QPalette, QPixmap
def __init__(self, parent=None):
super().__init__(parent)
self.setMinimumSize(200, 200)
- self.setFrameStyle(QFrame.Sunken | QFrame.StyledPanel)
- self.setAlignment(Qt.AlignCenter)
+ self.setFrameStyle(QFrame.Shadow.Sunken | QFrame.Shape.StyledPanel)
+ self.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.setAcceptDrops(True)
self.setAutoFillBackground(True)
self.clear()
def dragEnterEvent(self, event):
self.setText("<drop content>")
- self.setBackgroundRole(QPalette.Highlight)
+ self.setBackgroundRole(QPalette.ColorRole.Highlight)
event.acceptProposedAction()
self.changed.emit(event.mimeData())
self.setPixmap(QPixmap(mime_data.imageData()))
elif mime_data.hasFormat("text/markdown"):
self.setText(mime_data.data("text/markdown"))
- self.setTextFormat(Qt.MarkdownText)
+ self.setTextFormat(Qt.TextFormat.MarkdownText)
elif mime_data.hasHtml():
self.setText(mime_data.html())
- self.setTextFormat(Qt.RichText)
+ self.setTextFormat(Qt.TextFormat.RichText)
elif mime_data.hasText():
self.setText(mime_data.text())
- self.setTextFormat(Qt.PlainText)
+ self.setTextFormat(Qt.TextFormat.PlainText)
elif mime_data.hasUrls():
url_list = mime_data.urls()
text = ""
else:
self.setText("Cannot display data")
- self.setBackgroundRole(QPalette.Dark)
+ self.setBackgroundRole(QPalette.ColorRole.Dark)
event.acceptProposedAction()
def dragLeaveEvent(self, event):
@Slot()
def clear(self):
self.setText("<drop content>")
- self.setBackgroundRole(QPalette.Dark)
+ self.setBackgroundRole(QPalette.ColorRole.Dark)
self.changed.emit(None)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import re
from PySide6.QtCore import QMimeData, Qt, Slot
self._formats_table = QTableWidget()
self._formats_table.setColumnCount(2)
- self._formats_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
+ self._formats_table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
self._formats_table.setHorizontalHeaderLabels(["Format", "Content"])
self._formats_table.horizontalHeader().setStretchLastSection(True)
quit_button = QPushButton("Quit")
button_box = QDialogButtonBox()
- button_box.addButton(clear_button, QDialogButtonBox.ActionRole)
- button_box.addButton(self._copy_button, QDialogButtonBox.ActionRole)
+ button_box.addButton(clear_button, QDialogButtonBox.ButtonRole.ActionRole)
+ button_box.addButton(self._copy_button, QDialogButtonBox.ButtonRole.ActionRole)
self._copy_button.setVisible(False)
- button_box.addButton(quit_button, QDialogButtonBox.RejectRole)
+ button_box.addButton(quit_button, QDialogButtonBox.ButtonRole.RejectRole)
quit_button.clicked.connect(self.close)
clear_button.clicked.connect(drop_area.clear)
for format in mime_data.formats():
format_item = QTableWidgetItem(format)
- format_item.setFlags(Qt.ItemIsEnabled)
- format_item.setTextAlignment(Qt.AlignTop | Qt.AlignLeft)
+ format_item.setFlags(Qt.ItemFlag.ItemIsEnabled)
+ format_item.setTextAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignLeft)
if format == "text/plain":
text = simplify_whitespace(mime_data.text())
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QRect
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from pathlib import Path
self.index = 0
self._animation.setDuration(400)
- self._animation.setEasingCurve(QEasingCurve.InOutSine)
+ self._animation.setEasingCurve(QEasingCurve.Type.InOutSine)
- self.setRenderHint(QPainter.Antialiasing, True)
- self.setFrameStyle(QFrame.NoFrame)
+ self.setRenderHint(QPainter.RenderHint.Antialiasing, True)
+ self.setFrameStyle(QFrame.Shape.NoFrame)
@Property(float)
def index(self) -> float:
def keyPressEvent(self, event):
delta = 0
- if event.key() == Qt.Key_Left:
+ if event.key() == Qt.Key.Key_Left:
delta = -1
- elif event.key() == Qt.Key_Right:
+ elif event.key() == Qt.Key.Key_Right:
delta = 1
if self._animation.state() == QAbstractAnimation.Stopped and delta:
right = event.position().x() > (self.width() / 2)
delta = 1 if right else -1
- if self._animation.state() == QAbstractAnimation.Stopped:
+ if self._animation.state() == QAbstractAnimation.State.Stopped:
self._animation.setEndValue(self._index + delta)
self._animation.start()
event.accept()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/effects/blurpicker example from Qt v6.x"""
--- /dev/null
+Lighting Example
+================
+
+This example demonstrates a PySide6 application that creates a dynamic scene with lighting and
+shadow effects using `QGraphicsView` and `QGraphicsScene`. It features animated light sources and
+graphical items with drop shadows that respond to the light, showcasing advanced rendering and
+animation techniques.
+
+.. image:: lighting.webp
+ :width: 400
+ :alt: lighting screenshot
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import math
import sys
timer.setInterval(30)
timer.start()
- self.setRenderHint(QPainter.Antialiasing)
- self.setFrameStyle(QFrame.NoFrame)
+ self.setRenderHint(QPainter.RenderHint.Antialiasing)
+ self.setFrameStyle(QFrame.Shape.NoFrame)
def setup_scene(self):
self.m_scene.setSceneRect(-300, -200, 600, 460)
self.setBackgroundBrush(linear_grad)
radial_grad = QRadialGradient(30, 30, 30)
- radial_grad.setColorAt(0, Qt.yellow)
- radial_grad.setColorAt(0.2, Qt.yellow)
- radial_grad.setColorAt(1, Qt.transparent)
+ radial_grad.setColorAt(0, Qt.GlobalColor.yellow)
+ radial_grad.setColorAt(0.2, Qt.GlobalColor.yellow)
+ radial_grad.setColorAt(1, Qt.GlobalColor.transparent)
pixmap = QPixmap(60, 60)
- pixmap.fill(Qt.transparent)
+ pixmap.fill(Qt.GlobalColor.transparent)
with QPainter(pixmap) as painter:
- painter.setPen(Qt.NoPen)
+ painter.setPen(Qt.PenStyle.NoPen)
painter.setBrush(radial_grad)
painter.drawEllipse(0, 0, 60, 60)
else:
item = QGraphicsRectItem(0, 0, 50, 50)
- item.setPen(QPen(Qt.black, 1))
- item.setBrush(QBrush(Qt.white))
+ item.setPen(QPen(Qt.GlobalColor.black, 1))
+ item.setBrush(QBrush(Qt.GlobalColor.white))
effect = QGraphicsDropShadowEffect(self)
effect.setBlurRadius(8)
-.. _gettext-example:
-
GNU gettext Example
===================
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from pathlib import Path
import gettext
quit_action.triggered.connect(self.close)
self._list_widget = QListWidget()
- self._list_widget.setSelectionMode(QAbstractItemView.MultiSelection)
+ self._list_widget.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection)
self._list_widget.selectionModel().selectionChanged.connect(self.selection_changed)
self._list_widget.addItem("C++")
self._list_widget.addItem("Java")
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
w.setMinimumSize(minimum)
w.setPreferredSize(preferred)
w.setMaximumSize(maximum)
- w.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
+ w.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
return w
l = QGraphicsAnchorLayout() # noqa: E741
l.setSpacing(0)
- w = QGraphicsWidget(None, Qt.Window)
+ w = QGraphicsWidget(None, Qt.WindowType.Window)
w.setPos(20, 20)
w.setLayout(l)
- # Vertical.
- l.addAnchor(a, Qt.AnchorTop, l, Qt.AnchorTop)
- l.addAnchor(b, Qt.AnchorTop, l, Qt.AnchorTop)
+ # Vertical
+ l.addAnchor(a, Qt.AnchorPoint.AnchorTop, l, Qt.AnchorPoint.AnchorTop)
+ l.addAnchor(b, Qt.AnchorPoint.AnchorTop, l, Qt.AnchorPoint.AnchorTop)
- l.addAnchor(c, Qt.AnchorTop, a, Qt.AnchorBottom)
- l.addAnchor(c, Qt.AnchorTop, b, Qt.AnchorBottom)
- l.addAnchor(c, Qt.AnchorBottom, d, Qt.AnchorTop)
- l.addAnchor(c, Qt.AnchorBottom, e, Qt.AnchorTop)
+ l.addAnchor(c, Qt.AnchorPoint.AnchorTop, a, Qt.AnchorPoint.AnchorBottom)
+ l.addAnchor(c, Qt.AnchorPoint.AnchorTop, b, Qt.AnchorPoint.AnchorBottom)
+ l.addAnchor(c, Qt.AnchorPoint.AnchorBottom, d, Qt.AnchorPoint.AnchorTop)
+ l.addAnchor(c, Qt.AnchorPoint.AnchorBottom, e, Qt.AnchorPoint.AnchorTop)
- l.addAnchor(d, Qt.AnchorBottom, l, Qt.AnchorBottom)
- l.addAnchor(e, Qt.AnchorBottom, l, Qt.AnchorBottom)
+ l.addAnchor(d, Qt.AnchorPoint.AnchorBottom, l, Qt.AnchorPoint.AnchorBottom)
+ l.addAnchor(e, Qt.AnchorPoint.AnchorBottom, l, Qt.AnchorPoint.AnchorBottom)
- l.addAnchor(c, Qt.AnchorTop, f, Qt.AnchorTop)
- l.addAnchor(c, Qt.AnchorVerticalCenter, f, Qt.AnchorBottom)
- l.addAnchor(f, Qt.AnchorBottom, g, Qt.AnchorTop)
- l.addAnchor(c, Qt.AnchorBottom, g, Qt.AnchorBottom)
+ l.addAnchor(c, Qt.AnchorPoint.AnchorTop, f, Qt.AnchorPoint.AnchorTop)
+ l.addAnchor(c, Qt.AnchorPoint.AnchorVerticalCenter, f, Qt.AnchorPoint.AnchorBottom)
+ l.addAnchor(f, Qt.AnchorPoint.AnchorBottom, g, Qt.AnchorPoint.AnchorTop)
+ l.addAnchor(c, Qt.AnchorPoint.AnchorBottom, g, Qt.AnchorPoint.AnchorBottom)
# Horizontal.
- l.addAnchor(l, Qt.AnchorLeft, a, Qt.AnchorLeft)
- l.addAnchor(l, Qt.AnchorLeft, d, Qt.AnchorLeft)
- l.addAnchor(a, Qt.AnchorRight, b, Qt.AnchorLeft)
+ l.addAnchor(l, Qt.AnchorPoint.AnchorLeft, a, Qt.AnchorPoint.AnchorLeft)
+ l.addAnchor(l, Qt.AnchorPoint.AnchorLeft, d, Qt.AnchorPoint.AnchorLeft)
+ l.addAnchor(a, Qt.AnchorPoint.AnchorRight, b, Qt.AnchorPoint.AnchorLeft)
- l.addAnchor(a, Qt.AnchorRight, c, Qt.AnchorLeft)
- l.addAnchor(c, Qt.AnchorRight, e, Qt.AnchorLeft)
+ l.addAnchor(a, Qt.AnchorPoint.AnchorRight, c, Qt.AnchorPoint.AnchorLeft)
+ l.addAnchor(c, Qt.AnchorPoint.AnchorRight, e, Qt.AnchorPoint.AnchorLeft)
- l.addAnchor(b, Qt.AnchorRight, l, Qt.AnchorRight)
- l.addAnchor(e, Qt.AnchorRight, l, Qt.AnchorRight)
- l.addAnchor(d, Qt.AnchorRight, e, Qt.AnchorLeft)
+ l.addAnchor(b, Qt.AnchorPoint.AnchorRight, l, Qt.AnchorPoint.AnchorRight)
+ l.addAnchor(e, Qt.AnchorPoint.AnchorRight, l, Qt.AnchorPoint.AnchorRight)
+ l.addAnchor(d, Qt.AnchorPoint.AnchorRight, e, Qt.AnchorPoint.AnchorLeft)
- l.addAnchor(l, Qt.AnchorLeft, f, Qt.AnchorLeft)
- l.addAnchor(l, Qt.AnchorLeft, g, Qt.AnchorLeft)
- l.addAnchor(f, Qt.AnchorRight, g, Qt.AnchorRight)
+ l.addAnchor(l, Qt.AnchorPoint.AnchorLeft, f, Qt.AnchorPoint.AnchorLeft)
+ l.addAnchor(l, Qt.AnchorPoint.AnchorLeft, g, Qt.AnchorPoint.AnchorLeft)
+ l.addAnchor(f, Qt.AnchorPoint.AnchorRight, g, Qt.AnchorPoint.AnchorRight)
scene.addItem(w)
- scene.setBackgroundBrush(Qt.darkGreen)
+ scene.setBackgroundBrush(Qt.GlobalColor.darkGreen)
view = QGraphicsView(scene)
view.show()
--- /dev/null
+Anchor Layout Example
+=====================
+
+This application demonstrates the use of `QGraphicsAnchorLayout` in a PySide6 Widget application.
+It creates a graphical scene with several `QPushButton` widgets arranged using anchor constraints,
+allowing for dynamic and flexible layout management within a `QGraphicsView`. It demonstrates
+the analogous Qt example
+`Anchor Layout Example <https://doc.qt.io/qt-6.2/qtwidgets-graphicsview-anchorlayout-example.html>`_
+
+.. image:: anchorlayout.webp
+ :width: 400
+ :alt: anchorlayout screenshot
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import math
import sys
painter.drawEllipse(-10, -20, 20, 40)
# Eyes.
- painter.setBrush(Qt.white)
+ painter.setBrush(Qt.GlobalColor.white)
painter.drawEllipse(-10, -17, 8, 8)
painter.drawEllipse(2, -17, 8, 8)
# Nose.
- painter.setBrush(Qt.black)
+ painter.setBrush(Qt.GlobalColor.black)
painter.drawEllipse(QRectF(-2, -22, 4, 4))
# Pupils.
# Ears.
if self.scene().collidingItems(self):
- painter.setBrush(Qt.red)
+ painter.setBrush(Qt.GlobalColor.red)
else:
- painter.setBrush(Qt.darkYellow)
+ painter.setBrush(Qt.GlobalColor.darkYellow)
painter.drawEllipse(-17, -12, 16, 16)
painter.drawEllipse(1, -12, 16, 16)
path.cubicTo(-5, 22, -5, 22, 0, 25)
path.cubicTo(5, 27, 5, 32, 0, 30)
path.cubicTo(-5, 32, -5, 42, 0, 35)
- painter.setBrush(Qt.NoBrush)
+ painter.setBrush(Qt.BrushStyle.NoBrush)
painter.drawPath(path)
def advance(self, phase):
scene = QGraphicsScene()
scene.setSceneRect(-300, -300, 600, 600)
- scene.setItemIndexMethod(QGraphicsScene.NoIndex)
+ scene.setItemIndexMethod(QGraphicsScene.ItemIndexMethod.NoIndex)
for i in range(MOUSE_COUNT):
mouse = Mouse()
scene.addItem(mouse)
view = QGraphicsView(scene)
- view.setRenderHint(QPainter.Antialiasing)
+ view.setRenderHint(QPainter.RenderHint.Antialiasing)
view.setBackgroundBrush(QBrush(QPixmap(':/images/cheese.jpg')))
- view.setCacheMode(QGraphicsView.CacheBackground)
- view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
- view.setDragMode(QGraphicsView.ScrollHandDrag)
+ view.setCacheMode(QGraphicsView.CacheModeFlag.CacheBackground)
+ view.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.BoundingRectViewportUpdate)
+ view.setDragMode(QGraphicsView.DragMode.ScrollHandDrag)
view.setWindowTitle("Colliding Mice")
view.resize(400, 300)
view.show()
--- /dev/null
+Colliding Mice Example
+======================
+
+This application, is a graphical simulation built using PySide6. It creates a
+scene with multiple animated mouse objects that move around and interact with
+each other within a `QGraphicsView`. The mice change direction to avoid
+collisions and exhibit random movements, demonstrating the use of
+`QGraphicsItem`, `QGraphicsScene`, and `QGraphicsView` for creating dynamic and
+interactive graphics in a PySide6 application. This example demonstrates the
+analogous Qt example `Colliding Mice Example
+<https://doc.qt.io/qt-6/qtwidgets-graphicsview-collidingmice-example.html>`_.
+
+.. image:: collidingmice.webp
+ :width: 400
+ :alt: collidingmice screenshot
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
# Resource object code
#
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import math
import sys
self._my_start_item = startItem
self._my_end_item = endItem
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
- self._my_color = Qt.black
+ self._my_color = Qt.GlobalColor.black
self.setPen(QPen(self._my_color, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
def set_color(self, color):
def __init__(self, parent=None, scene=None):
super().__init__(parent, scene)
- self.setFlag(QGraphicsItem.ItemIsMovable)
- self.setFlag(QGraphicsItem.ItemIsSelectable)
+ self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable)
+ self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsSelectable)
def itemChange(self, change, value):
- if change == QGraphicsItem.ItemSelectedChange:
+ if change == QGraphicsItem.GraphicsItemChange.ItemSelectedChange:
self.selected_change.emit(self)
return value
def focusOutEvent(self, event):
- self.setTextInteractionFlags(Qt.NoTextInteraction)
+ self.setTextInteractionFlags(Qt.TextInteractionFlag.NoTextInteraction)
self.lost_focus.emit(self)
super(DiagramTextItem, self).focusOutEvent(event)
def mouseDoubleClickEvent(self, event):
- if self.textInteractionFlags() == Qt.NoTextInteraction:
- self.setTextInteractionFlags(Qt.TextEditorInteraction)
+ if self.textInteractionFlags() == Qt.TextInteractionFlag.NoTextInteraction:
+ self.setTextInteractionFlags(Qt.TextInteractionFlag.TextEditorInteraction)
super(DiagramTextItem, self).mouseDoubleClickEvent(event)
QPointF(-120, -80)])
self.setPolygon(self._my_polygon)
- self.setFlag(QGraphicsItem.ItemIsMovable, True)
- self.setFlag(QGraphicsItem.ItemIsSelectable, True)
+ self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable, True)
+ self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsSelectable, True)
def remove_arrow(self, arrow):
try:
def image(self):
pixmap = QPixmap(250, 250)
- pixmap.fill(Qt.transparent)
+ pixmap.fill(Qt.GlobalColor.transparent)
with QPainter(pixmap) as painter:
- painter.setPen(QPen(Qt.black, 8))
+ painter.setPen(QPen(Qt.GlobalColor.black, 8))
painter.translate(125, 125)
painter.drawPolyline(self._my_polygon)
return pixmap
self._my_context_menu.exec(event.screenPos())
def itemChange(self, change, value):
- if change == QGraphicsItem.ItemPositionChange:
+ if change == QGraphicsItem.GraphicsItemChange.ItemPositionChange:
for arrow in self.arrows:
arrow.updatePosition()
self._my_item_type = DiagramItem.Step
self.line = None
self._text_item = None
- self._my_item_color = Qt.white
- self._my_text_color = Qt.black
- self._my_line_color = Qt.black
+ self._my_item_color = Qt.GlobalColor.white
+ self._my_text_color = Qt.GlobalColor.black
+ self._my_line_color = Qt.GlobalColor.black
self._my_font = QFont()
def set_line_color(self, color):
item.deleteLater()
def mousePressEvent(self, mouseEvent):
- if (mouseEvent.button() != Qt.LeftButton):
+ if (mouseEvent.button() != Qt.MouseButton.LeftButton):
return
if self._my_mode == self.InsertItem:
elif self._my_mode == self.InsertText:
text_item = DiagramTextItem()
text_item.setFont(self._my_font)
- text_item.setTextInteractionFlags(Qt.TextEditorInteraction)
+ text_item.setTextInteractionFlags(Qt.TextInteractionFlag.TextEditorInteraction)
text_item.setZValue(1000.0)
text_item.lost_focus.connect(self.editor_lost_focus)
text_item.selected_change.connect(self.item_selected)
font = item.font()
self._font_combo.setCurrentFont(font)
self._font_size_combo.setEditText(str(font.pointSize()))
- self._bold_action.setChecked(font.weight() == QFont.Bold)
+ self._bold_action.setChecked(font.weight() == QFont.Weight.Bold)
self._italic_action.setChecked(font.italic())
self._underline_action.setChecked(font.underline())
text_button.setIconSize(QSize(50, 50))
text_layout = QGridLayout()
- text_layout.addWidget(text_button, 0, 0, Qt.AlignHCenter)
- text_layout.addWidget(QLabel("Text"), 1, 0, Qt.AlignCenter)
+ text_layout.addWidget(text_button, 0, 0, Qt.AlignmentFlag.AlignHCenter)
+ text_layout.addWidget(QLabel("Text"), 1, 0, Qt.AlignmentFlag.AlignCenter)
text_widget = QWidget()
text_widget.setLayout(text_layout)
layout.addWidget(text_widget, 1, 1)
background_widget.setLayout(background_layout)
self._tool_box = QToolBox()
- self._tool_box.setSizePolicy(QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Ignored))
+ self._tool_box.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Maximum,
+ QSizePolicy.Policy.Ignored))
self._tool_box.setMinimumWidth(item_widget.sizeHint().width())
self._tool_box.addItem(item_widget, "Basic Flowchart Shapes")
self._tool_box.addItem(background_widget, "Backgrounds")
self._font_size_combo.currentIndexChanged.connect(self.font_size_changed)
self._font_color_tool_button = QToolButton()
- self._font_color_tool_button.setPopupMode(QToolButton.MenuButtonPopup)
+ self._font_color_tool_button.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)
self._font_color_tool_button.setMenu(
- self.create_color_menu(self.text_color_changed, Qt.black))
+ self.create_color_menu(self.text_color_changed, Qt.GlobalColor.black))
self._text_action = self._font_color_tool_button.menu().defaultAction()
self._font_color_tool_button.setIcon(
- self.create_color_tool_button_icon(':/images/textpointer.png', Qt.black))
+ self.create_color_tool_button_icon(':/images/textpointer.png', Qt.GlobalColor.black))
self._font_color_tool_button.setAutoFillBackground(True)
self._font_color_tool_button.clicked.connect(self.text_button_triggered)
self._fill_color_tool_button = QToolButton()
- self._fill_color_tool_button.setPopupMode(QToolButton.MenuButtonPopup)
+ self._fill_color_tool_button.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)
self._fill_color_tool_button.setMenu(
- self.create_color_menu(self.item_color_changed, Qt.white))
+ self.create_color_menu(self.item_color_changed, Qt.GlobalColor.white))
self._fill_action = self._fill_color_tool_button.menu().defaultAction()
self._fill_color_tool_button.setIcon(
- self.create_color_tool_button_icon(':/images/floodfill.png', Qt.white))
+ self.create_color_tool_button_icon(':/images/floodfill.png', Qt.GlobalColor.white))
self._fill_color_tool_button.clicked.connect(self.fill_button_triggered)
self._line_color_tool_button = QToolButton()
- self._line_color_tool_button.setPopupMode(QToolButton.MenuButtonPopup)
+ self._line_color_tool_button.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)
self._line_color_tool_button.setMenu(
- self.create_color_menu(self.line_color_changed, Qt.black))
+ self.create_color_menu(self.line_color_changed, Qt.GlobalColor.black))
self._line_action = self._line_color_tool_button.menu().defaultAction()
self._line_color_tool_button.setIcon(
- self.create_color_tool_button_icon(':/images/linecolor.png', Qt.black))
+ self.create_color_tool_button_icon(':/images/linecolor.png', Qt.GlobalColor.black))
self._line_color_tool_button.clicked.connect(self.line_button_triggered)
self._text_tool_bar = self.addToolBar("Font")
self._background_button_group.addButton(button)
layout = QGridLayout()
- layout.addWidget(button, 0, 0, Qt.AlignHCenter)
- layout.addWidget(QLabel(text), 1, 0, Qt.AlignCenter)
+ layout.addWidget(button, 0, 0, Qt.AlignmentFlag.AlignHCenter)
+ layout.addWidget(QLabel(text), 1, 0, Qt.AlignmentFlag.AlignCenter)
widget = QWidget()
widget.setLayout(layout)
self._button_group.addButton(button, diagram_type)
layout = QGridLayout()
- layout.addWidget(button, 0, 0, Qt.AlignHCenter)
- layout.addWidget(QLabel(text), 1, 0, Qt.AlignCenter)
+ layout.addWidget(button, 0, 0, Qt.AlignmentFlag.AlignHCenter)
+ layout.addWidget(QLabel(text), 1, 0, Qt.AlignmentFlag.AlignCenter)
widget = QWidget()
widget.setLayout(layout)
return widget
def create_color_menu(self, slot, defaultColor):
- colors = [Qt.black, Qt.white, Qt.red, Qt.blue, Qt.yellow]
+ colors = [Qt.GlobalColor.black, Qt.GlobalColor.white, Qt.GlobalColor.red,
+ Qt.GlobalColor.blue, Qt.GlobalColor.yellow]
names = ["black", "white", "red", "blue", "yellow"]
color_menu = QMenu(self)
def create_color_tool_button_icon(self, imageFile, color):
pixmap = QPixmap(50, 80)
- pixmap.fill(Qt.transparent)
+ pixmap.fill(Qt.GlobalColor.transparent)
with QPainter(pixmap) as painter:
image = QPixmap(imageFile)
pixmap = QPixmap(20, 20)
with QPainter(pixmap) as painter:
- painter.setPen(Qt.NoPen)
+ painter.setPen(Qt.PenStyle.NoPen)
painter.fillRect(QRect(0, 0, 20, 20), color)
return QIcon(pixmap)
--- /dev/null
+Diagram Scene Example
+=====================
+
+This example demonstrates how to create an interactive diagram editor using
+PySide6, featuring various diagram shapes, connectors, and customizable
+properties. It demonstrates the analogous Qt example `Diagram Scene Example
+<https://doc.qt.io/qt-6/qtwidgets-graphicsview-diagramscene-example.html>`_.
+
+.. image:: diagramscene.webp
+ :width: 400
+ :alt: diagramscene screenshot
--- /dev/null
+Drag and Drop Robot Example
+===========================
+
+This example demonstrates a PySide6 application where users can drag and drop
+color items onto a robot graphic, showcasing interactive drag-and-drop
+functionality within a `QGraphicsView`. It demonstrates the analogous Qt example
+`Drag and Drop Robot Example
+<https://doc.qt.io/qt-6/qtwidgets-graphicsview-dragdroprobot-example.html>`_.
+
+.. image:: dragdroprobot.png
+ :width: 400
+ :alt: dragdroprobot screenshot
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
import math
(r, g, b) = (self.color.red(), self.color.green(), self.color.blue())
self.setToolTip(
f"QColor({r}, {g}, {b})\nClick and drag this color onto the robot!")
- self.setCursor(Qt.OpenHandCursor)
+ self.setCursor(Qt.CursorShape.OpenHandCursor)
self._start_drag_distance = QApplication.startDragDistance()
def boundingRect(self):
return QRectF(-15.5, -15.5, 34, 34)
def paint(self, painter, option, widget):
- painter.setPen(Qt.NoPen)
- painter.setBrush(Qt.darkGray)
+ painter.setPen(Qt.PenStyle.NoPen)
+ painter.setBrush(Qt.GlobalColor.darkGray)
painter.drawEllipse(-12, -12, 30, 30)
- painter.setPen(QPen(Qt.black, 1))
+ painter.setPen(QPen(Qt.GlobalColor.black, 1))
painter.setBrush(QBrush(self.color))
painter.drawEllipse(-15, -15, 30, 30)
def mousePressEvent(self, event):
- if event.button() != Qt.LeftButton:
+ if event.button() != Qt.MouseButton.LeftButton:
event.ignore()
return
- self.setCursor(Qt.ClosedHandCursor)
+ self.setCursor(Qt.CursorShape.ClosedHandCursor)
def mouseMoveEvent(self, event):
- start = QPointF(event.buttonDownScreenPos(Qt.LeftButton))
+ start = QPointF(event.buttonDownScreenPos(Qt.MouseButton.LeftButton))
if QLineF(event.screenPos(), start).length() < self._start_drag_distance:
return
mime.setText(f"#{r:02x}{g:02x}{b:02x}")
pixmap = QPixmap(34, 34)
- pixmap.fill(Qt.white)
+ pixmap.fill(Qt.GlobalColor.white)
with QPainter(pixmap) as painter:
painter.translate(15, 15)
- painter.setRenderHint(QPainter.Antialiasing)
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
self.paint(painter, None, None)
pixmap.setMask(pixmap.createHeuristicMask())
drag.setHotSpot(QPoint(15, 20))
drag.exec()
- self.setCursor(Qt.OpenHandCursor)
+ self.setCursor(Qt.CursorShape.OpenHandCursor)
def mouseReleaseEvent(self, event):
- self.setCursor(Qt.OpenHandCursor)
+ self.setCursor(Qt.CursorShape.OpenHandCursor)
class RobotPart(QGraphicsItem):
def __init__(self, parent=None):
super().__init__(parent)
- self.color = QColor(Qt.lightGray)
+ self.color = QColor(Qt.GlobalColor.lightGray)
self.pixmap = None
self._drag_over = False
def paint(self, painter, option, widget=None):
if not self.pixmap:
painter.setBrush(self._drag_over and self.color.lighter(130) or self.color)
- painter.drawRoundedRect(-10, -30, 20, 30, 25, 25, Qt.RelativeSize)
- painter.setBrush(Qt.white)
+ painter.drawRoundedRect(-10, -30, 20, 30, 25, 25, Qt.SizeMode.RelativeSize)
+ painter.setBrush(Qt.GlobalColor.white)
painter.drawEllipse(-7, -3 - 20, 7, 7)
painter.drawEllipse(0, -3 - 20, 7, 7)
- painter.setBrush(Qt.black)
+ painter.setBrush(Qt.GlobalColor.black)
painter.drawEllipse(-5, -1 - 20, 2, 2)
painter.drawEllipse(2, -1 - 20, 2, 2)
- painter.setPen(QPen(Qt.black, 2))
- painter.setBrush(Qt.NoBrush)
+ painter.setPen(QPen(Qt.GlobalColor.black, 2))
+ painter.setBrush(Qt.BrushStyle.NoBrush)
painter.drawArc(-6, -2 - 20, 12, 15, 190 * 16, 160 * 16)
else:
painter.scale(.2272, .2824)
def paint(self, painter, option, widget=None):
painter.setBrush(self._drag_over and self.color.lighter(130)
or self.color)
- painter.drawRoundedRect(-20, -20, 40, 60, 25, 25, Qt.RelativeSize)
+ painter.drawRoundedRect(-20, -20, 40, 60, 25, 25, Qt.SizeMode.RelativeSize)
painter.drawEllipse(-25, -20, 20, 20)
painter.drawEllipse(5, -20, 20, 20)
painter.drawEllipse(-20, 22, 20, 20)
def paint(self, painter, option, widget=None):
painter.setBrush(self._drag_over and self.color.lighter(130) or self.color)
painter.drawRoundedRect(self.boundingRect(), 50, 50,
- Qt.RelativeSize)
+ Qt.SizeMode.RelativeSize)
painter.drawEllipse(-5, -5, 10, 10)
self.animations[0].setScaleAt(1, 1.1, 1.1)
self.timeline.setUpdateInterval(1000 / 25)
- curve = QEasingCurve(QEasingCurve.SineCurve)
+ curve = QEasingCurve(QEasingCurve.Type.SineCurve)
self.timeline.setEasingCurve(curve)
self.timeline.setLoopCount(0)
self.timeline.setDuration(2000)
scene.addItem(robot)
view = QGraphicsView(scene)
- view.setRenderHint(QPainter.Antialiasing)
- view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
+ view.setRenderHint(QPainter.RenderHint.Antialiasing)
+ view.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.BoundingRectViewportUpdate)
view.setBackgroundBrush(QColor(230, 200, 167))
view.setWindowTitle("Drag and Drop Robot")
view.show()
--- /dev/null
+Elastic Nodes Example
+=====================
+
+This example demonstrates a PySide6 application that creates a dynamic and
+interactive graphical scene with nodes connected by elastic edges. The nodes
+and edges respond to user interactions and physics-based movements, showcasing
+the use of `QGraphicsItem`, `QGraphicsScene`, and `QGraphicsView` for creating
+complex animations and interactions.
+
+It demonstrates the analogous Qt example `Elastic Nodes Example
+<https://doc.qt.io/qt-6/qtwidgets-graphicsview-elasticnodes-example.html>`_.
+
+.. image:: elasticnodes.png
+ :width: 400
+ :alt: elasticnodes screenshot
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
import weakref
self._arrow_size = 10.0
self._source_point = QPointF()
self._dest_point = QPointF()
- self.setAcceptedMouseButtons(Qt.NoButton)
+ self.setAcceptedMouseButtons(Qt.MouseButton.NoButton)
self.source = weakref.ref(sourceNode)
self.dest = weakref.ref(destNode)
self.source().add_edge(self)
if line.length() == 0.0:
return
- painter.setPen(QPen(Qt.black, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
+ painter.setPen(QPen(Qt.GlobalColor.black, 1, Qt.PenStyle.SolidLine,
+ Qt.PenCapStyle.RoundCap, Qt.PenJoinStyle.RoundJoin))
painter.drawLine(line)
# Draw the arrows if there's enough room.
math.cos(angle - math.pi + math.pi / 3) * self._arrow_size)
dest_arrow_p2 = self._dest_point + arrow_head2
- painter.setBrush(Qt.black)
+ painter.setBrush(Qt.GlobalColor.black)
painter.drawPolygon(QPolygonF([line.p1(), source_arrow_p1, source_arrow_p2]))
painter.drawPolygon(QPolygonF([line.p2(), dest_arrow_p1, dest_arrow_p2]))
self.graph = weakref.ref(graphWidget)
self._edge_list = []
self._new_pos = QPointF()
- self.setFlag(QGraphicsItem.ItemIsMovable)
- self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
- self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
+ self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable)
+ self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemSendsGeometryChanges)
+ self.setCacheMode(QGraphicsItem.CacheMode.DeviceCoordinateCache)
self.setZValue(-1)
def item_type(self):
return path
def paint(self, painter, option, widget):
- painter.setPen(Qt.NoPen)
- painter.setBrush(Qt.darkGray)
+ painter.setPen(Qt.PenStyle.NoPen)
+ painter.setBrush(Qt.GlobalColor.darkGray)
painter.drawEllipse(-7, -7, 20, 20)
gradient = QRadialGradient(-3, -3, 10)
- if option.state & QStyle.State_Sunken:
+ if option.state & QStyle.StateFlag.State_Sunken:
gradient.setCenter(3, 3)
gradient.setFocalPoint(3, 3)
- gradient.setColorAt(1, QColor(Qt.yellow).lighter(120))
- gradient.setColorAt(0, QColor(Qt.darkYellow).lighter(120))
+ gradient.setColorAt(1, QColor(Qt.GlobalColor.yellow).lighter(120))
+ gradient.setColorAt(0, QColor(Qt.GlobalColor.darkYellow).lighter(120))
else:
- gradient.setColorAt(0, Qt.yellow)
- gradient.setColorAt(1, Qt.darkYellow)
+ gradient.setColorAt(0, Qt.GlobalColor.yellow)
+ gradient.setColorAt(1, Qt.GlobalColor.darkYellow)
painter.setBrush(QBrush(gradient))
- painter.setPen(QPen(Qt.black, 0))
+ painter.setPen(QPen(Qt.GlobalColor.black, 0))
painter.drawEllipse(-10, -10, 20, 20)
def itemChange(self, change, value):
- if change == QGraphicsItem.ItemPositionChange:
+ if change == QGraphicsItem.GraphicsItemChange.ItemPositionChange:
for edge in self._edge_list:
edge().adjust()
self.graph().item_moved()
self._timer_id = 0
scene = QGraphicsScene(self)
- scene.setItemIndexMethod(QGraphicsScene.NoIndex)
+ scene.setItemIndexMethod(QGraphicsScene.ItemIndexMethod.NoIndex)
scene.setSceneRect(-200, -200, 400, 400)
self.setScene(scene)
- self.setCacheMode(QGraphicsView.CacheBackground)
- self.setRenderHint(QPainter.Antialiasing)
- self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
- self.setResizeAnchor(QGraphicsView.AnchorViewCenter)
+ self.setCacheMode(QGraphicsView.CacheModeFlag.CacheBackground)
+ self.setRenderHint(QPainter.RenderHint.Antialiasing)
+ self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse)
+ self.setResizeAnchor(QGraphicsView.ViewportAnchor.AnchorViewCenter)
node1 = Node(self)
node2 = Node(self)
def keyPressEvent(self, event):
key = event.key()
- if key == Qt.Key_Up:
+ if key == Qt.Key.Key_Up:
self._center_node.moveBy(0, -20)
- elif key == Qt.Key_Down:
+ elif key == Qt.Key.Key_Down:
self._center_node.moveBy(0, 20)
- elif key == Qt.Key_Left:
+ elif key == Qt.Key.Key_Left:
self._center_node.moveBy(-20, 0)
- elif key == Qt.Key_Right:
+ elif key == Qt.Key.Key_Right:
self._center_node.moveBy(20, 0)
- elif key == Qt.Key_Plus:
+ elif key == Qt.Key.Key_Plus:
self.scale_view(1.2)
- elif key == Qt.Key_Minus:
+ elif key == Qt.Key.Key_Minus:
self.scale_view(1 / 1.2)
- elif key == Qt.Key_Space or key == Qt.Key_Enter:
+ elif key == Qt.Key.Key_Space or key == Qt.Key.Key_Enter:
for item in self.scene().items():
if isinstance(item, Node):
item.setPos(-150 + random(300), -150 + random(300))
# Fill.
gradient = QLinearGradient(scene_rect.topLeft(), scene_rect.bottomRight())
- gradient.setColorAt(0, Qt.white)
+ gradient.setColorAt(0, Qt.GlobalColor.white)
gradient.setColorAt(1, Qt.lightGray)
painter.fillRect(rect.intersected(scene_rect), QBrush(gradient))
painter.setBrush(Qt.NoBrush)
painter.setFont(font)
painter.setPen(Qt.lightGray)
painter.drawText(text_rect.translated(2, 2), message)
- painter.setPen(Qt.black)
+ painter.setPen(Qt.GlobalColor.black)
painter.drawText(text_rect, message)
def scale_view(self, scaleFactor):
--- /dev/null
+Image Viewer Example
+====================
+
+This example demonstrates an image viewer application built using PySide6,
+featuring functionalities such as opening, saving, printing, copying, pasting,
+and zooming images within a scrollable area. This example demonstrates the
+analogous Qt example `Image Viewer Example
+<https://doc.qt.io/qt-6.2/qtwidgets-widgets-imageviewer-example.html>`_.
+
+.. image:: imageviewer.webp
+ :width: 400
+ :alt: imageviewer screenshot
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtPrintSupport import QPrintDialog, QPrinter
from PySide6.QtWidgets import (QApplication, QDialog, QFileDialog, QLabel,
self._scale_factor = 1.0
self._first_file_dialog = True
self._image_label = QLabel()
- self._image_label.setBackgroundRole(QPalette.Base)
- self._image_label.setSizePolicy(QSizePolicy.Ignored,
- QSizePolicy.Ignored)
+ self._image_label.setBackgroundRole(QPalette.ColorRole.Base)
+ self._image_label.setSizePolicy(QSizePolicy.Policy.Ignored,
+ QSizePolicy.Policy.Ignored)
self._image_label.setScaledContents(True)
self._scroll_area = QScrollArea()
- self._scroll_area.setBackgroundRole(QPalette.Dark)
+ self._scroll_area.setBackgroundRole(QPalette.ColorRole.Dark)
self._scroll_area.setWidget(self._image_label)
self._scroll_area.setVisible(False)
self.setCentralWidget(self._scroll_area)
def _set_image(self, new_image):
self._image = new_image
if self._image.colorSpace().isValid():
- self._image.convertToColorSpace(QColorSpace.SRgb)
+ color_space = QColorSpace(QColorSpace.NamedColorSpace.SRgb)
+ self._image.convertToColorSpace(color_space)
self._image_label.setPixmap(QPixmap.fromImage(self._image))
self._scale_factor = 1.0
@Slot()
def _open(self):
dialog = QFileDialog(self, "Open File")
- self._initialize_image_filedialog(dialog, QFileDialog.AcceptOpen)
- while (dialog.exec() == QDialog.Accepted
+ self._initialize_image_filedialog(dialog, QFileDialog.AcceptMode.AcceptOpen)
+ while (dialog.exec() == QDialog.DialogCode.Accepted
and not self.load_file(dialog.selectedFiles()[0])):
pass
@Slot()
def _save_as(self):
dialog = QFileDialog(self, "Save File As")
- self._initialize_image_filedialog(dialog, QFileDialog.AcceptSave)
- while (dialog.exec() == QDialog.Accepted
+ self._initialize_image_filedialog(dialog, QFileDialog.AcceptMode.AcceptSave)
+ while (dialog.exec() == QDialog.DialogCode.Accepted
and not self._save_file(dialog.selectedFiles()[0])):
pass
def _print_(self):
printer = QPrinter()
dialog = QPrintDialog(printer, self)
- if dialog.exec() == QDialog.Accepted:
+ if dialog.exec() == QDialog.DialogCode.Accepted:
with QPainter(printer) as painter:
pixmap = self._image_label.pixmap()
rect = painter.viewport()
self._open_act = file_menu.addAction("&Open...")
self._open_act.triggered.connect(self._open)
- self._open_act.setShortcut(QKeySequence.Open)
+ self._open_act.setShortcut(QKeySequence.StandardKey.Open)
self._save_as_act = file_menu.addAction("&Save As...")
self._save_as_act.triggered.connect(self._save_as)
self._print_act = file_menu.addAction("&Print...")
self._print_act.triggered.connect(self._print_)
- self._print_act.setShortcut(QKeySequence.Print)
+ self._print_act.setShortcut(QKeySequence.StandardKey.Print)
self._print_act.setEnabled(False)
file_menu.addSeparator()
self._copy_act = edit_menu.addAction("&Copy")
self._copy_act.triggered.connect(self._copy)
- self._copy_act.setShortcut(QKeySequence.Copy)
+ self._copy_act.setShortcut(QKeySequence.StandardKey.Copy)
self._copy_act.setEnabled(False)
self._paste_act = edit_menu.addAction("&Paste")
self._paste_act.triggered.connect(self._paste)
- self._paste_act.setShortcut(QKeySequence.Paste)
+ self._paste_act.setShortcut(QKeySequence.StandardKey.Paste)
view_menu = self.menuBar().addMenu("&View")
self._zoom_in_act = view_menu.addAction("Zoom &In (25%)")
- self._zoom_in_act.setShortcut(QKeySequence.ZoomIn)
+ self._zoom_in_act.setShortcut(QKeySequence.StandardKey.ZoomIn)
self._zoom_in_act.triggered.connect(self._zoom_in)
self._zoom_in_act.setEnabled(False)
self._zoom_out_act = view_menu.addAction("Zoom &Out (25%)")
self._zoom_out_act.triggered.connect(self._zoom_out)
- self._zoom_out_act.setShortcut(QKeySequence.ZoomOut)
+ self._zoom_out_act.setShortcut(QKeySequence.StandardKey.ZoomOut)
self._zoom_out_act.setEnabled(False)
self._normal_size_act = view_menu.addAction("&Normal Size")
def _initialize_image_filedialog(self, dialog, acceptMode):
if self._first_file_dialog:
self._first_file_dialog = False
- locations = QStandardPaths.standardLocations(QStandardPaths.PicturesLocation)
+ locations = QStandardPaths.standardLocations(QStandardPaths.StandardLocation.PicturesLocation) # noqa: E501
directory = locations[-1] if locations else QDir.currentPath()
dialog.setDirectory(directory)
dialog.setMimeTypeFilters(mime_types)
dialog.selectMimeTypeFilter("image/jpeg")
dialog.setAcceptMode(acceptMode)
- if acceptMode == QFileDialog.AcceptSave:
+ if acceptMode == QFileDialog.AcceptMode.AcceptSave:
dialog.setDefaultSuffix("jpg")
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/imageviewer example from Qt v6.0"""
# Copyright (C) 2011 Arun Srinivasan <rulfzid@gmail.com>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Qt
from PySide6.QtWidgets import (QDialog, QLabel, QTextEdit, QLineEdit,
name_label = QLabel("Name")
address_label = QLabel("Address")
- button_box = QDialogButtonBox(QDialogButtonBox.Ok
- | QDialogButtonBox.Cancel)
+ button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok
+ | QDialogButtonBox.StandardButton.Cancel)
self._name_text = QLineEdit()
self._address_text = QTextEdit()
grid.setColumnStretch(1, 2)
grid.addWidget(name_label, 0, 0)
grid.addWidget(self._name_text, 0, 1)
- grid.addWidget(address_label, 1, 0, Qt.AlignLeft | Qt.AlignTop)
- grid.addWidget(self._address_text, 1, 1, Qt.AlignLeft)
+ grid.addWidget(address_label, 1, 0, Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
+ grid.addWidget(self._address_text, 1, 1, Qt.AlignmentFlag.AlignLeft)
layout = QVBoxLayout()
layout.addLayout(grid)
# Copyright (C) 2011 Arun Srinivasan <rulfzid@gmail.com>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Slot
from PySide6.QtGui import QAction
# Copyright (C) 2011 Arun Srinivasan <rulfzid@gmail.com>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
try:
import cpickle as pickle
# Step 2: get the index of the newly created row and use it.
# to set the name
ix = self._table_model.index(0, 0, QModelIndex())
- self._table_model.setData(ix, address["name"], Qt.EditRole)
+ self._table_model.setData(ix, address["name"], Qt.ItemDataRole.EditRole)
# Step 3: lather, rinse, repeat for the address.
ix = self._table_model.index(0, 1, QModelIndex())
- self._table_model.setData(ix, address["address"], Qt.EditRole)
+ self._table_model.setData(ix, address["address"], Qt.ItemDataRole.EditRole)
# Remove the newAddressTab, as we now have at least one
# address in the model.
row = proxy_model.mapToSource(indexes[0]).row()
ix = self._table_model.index(row, 0, QModelIndex())
- name = self._table_model.data(ix, Qt.DisplayRole)
+ name = self._table_model.data(ix, Qt.ItemDataRole.DisplayRole)
ix = self._table_model.index(row, 1, QModelIndex())
- address = self._table_model.data(ix, Qt.DisplayRole)
+ address = self._table_model.data(ix, Qt.ItemDataRole.DisplayRole)
# Open an addDialogWidget, and only allow the user to edit the address.
add_dialog = AddDialogWidget()
new_address = add_dialog.address
if new_address != address:
ix = self._table_model.index(row, 1, QModelIndex())
- self._table_model.setData(ix, new_address, Qt.EditRole)
+ self._table_model.setData(ix, new_address, Qt.ItemDataRole.EditRole)
@Slot()
def remove_entry(self):
table_view = QTableView()
table_view.setModel(proxy_model)
table_view.setSortingEnabled(True)
- table_view.setSelectionBehavior(QAbstractItemView.SelectRows)
+ table_view.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
table_view.horizontalHeader().setStretchLastSection(True)
table_view.verticalHeader().hide()
- table_view.setEditTriggers(QAbstractItemView.NoEditTriggers)
- table_view.setSelectionMode(QAbstractItemView.SingleSelection)
+ table_view.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
+ table_view.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
# This here be the magic: we use the group name (e.g. "ABC") to
# build the regex for the QSortFilterProxyModel for the group's
# "A", "B", or "C". Notice that we set it to be case-insensitive.
re = QRegularExpression(f"^[{group}].*")
assert re.isValid()
- re.setPatternOptions(QRegularExpression.CaseInsensitiveOption)
+ re.setPatternOptions(QRegularExpression.PatternOption.CaseInsensitiveOption)
proxy_model.setFilterRegularExpression(re)
proxy_model.setFilterKeyColumn(0) # Filter on the "name" column
- proxy_model.sort(0, Qt.AscendingOrder)
+ proxy_model.sort(0, Qt.SortOrder.AscendingOrder)
# This prevents an application crash (see:
# https://www.qtcentre.org/threads/58874-QListView-SelectionModel-selectionChanged-Crash) # noqa: E501
# Copyright (C) 2011 Arun Srinivasan <rulfzid@gmail.com>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import (Qt, Signal)
from PySide6.QtWidgets import (QWidget, QLabel, QPushButton, QVBoxLayout)
layout = QVBoxLayout()
layout.addWidget(description_label)
- layout.addWidget(add_button, 0, Qt.AlignCenter)
+ layout.addWidget(add_button, 0, Qt.AlignmentFlag.AlignCenter)
self.setLayout(layout)
# Copyright (C) 2011 Arun Srinivasan <rulfzid@gmail.com>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import (Qt, QAbstractTableModel, QModelIndex)
""" Returns the number of columns the model holds. """
return 2
- def data(self, index, role=Qt.DisplayRole):
+ def data(self, index, role=Qt.ItemDataRole.DisplayRole):
""" Depending on the index and role given, return data. If not
returning data, return None (PySide equivalent of QT's
"invalid QVariant").
if not 0 <= index.row() < len(self.addresses):
return None
- if role == Qt.DisplayRole:
+ if role == Qt.ItemDataRole.DisplayRole:
name = self.addresses[index.row()]["name"]
address = self.addresses[index.row()]["address"]
return None
- def headerData(self, section, orientation, role=Qt.DisplayRole):
+ def headerData(self, section, orientation, role=Qt.ItemDataRole.DisplayRole):
""" Set the headers to be displayed. """
- if role != Qt.DisplayRole:
+ if role != Qt.ItemDataRole.DisplayRole:
return None
- if orientation == Qt.Horizontal:
+ if orientation == Qt.Orientation.Horizontal:
if section == 0:
return "Name"
elif section == 1:
self.endRemoveRows()
return True
- def setData(self, index, value, role=Qt.EditRole):
+ def setData(self, index, value, role=Qt.ItemDataRole.EditRole):
""" Adjust the data (set it to <value>) depending on the given
index and role.
"""
- if role != Qt.EditRole:
+ if role != Qt.ItemDataRole.EditRole:
return False
if index.isValid() and 0 <= index.row() < len(self.addresses):
manually adjust each tableView to have NoEditTriggers.
"""
if not index.isValid():
- return Qt.ItemIsEnabled
+ return Qt.ItemFlag.ItemIsEnabled
return Qt.ItemFlags(QAbstractTableModel.flags(self, index)
- | Qt.ItemIsEditable)
+ | Qt.ItemFlag.ItemIsEditable)
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtCore import (QDate, QDateTime, QRegularExpression,
self.setWindowTitle("Basic Sort/Filter Model")
self.resize(500, 450)
- self._proxy_view.sortByColumn(1, Qt.AscendingOrder)
+ self._proxy_view.sortByColumn(1, Qt.SortOrder.AscendingOrder)
self._filter_column_combo_box.setCurrentIndex(1)
self._filter_pattern_line_edit.setText("Andy|Grace")
reg_exp = QRegularExpression(pattern)
if not self._filter_case_sensitivity_check_box.isChecked():
options = reg_exp.patternOptions()
- options |= QRegularExpression.CaseInsensitiveOption
+ options |= QRegularExpression.PatternOption.CaseInsensitiveOption
reg_exp.setPatternOptions(options)
self._proxy_model.setFilterRegularExpression(reg_exp)
@Slot()
def sort_changed(self):
if self._sort_case_sensitivity_check_box.isChecked():
- case_sensitivity = Qt.CaseSensitive
+ case_sensitivity = Qt.CaseSensitivity.CaseSensitive
else:
- case_sensitivity = Qt.CaseInsensitive
+ case_sensitivity = Qt.CaseSensitivity.CaseInsensitive
self._proxy_model.setSortCaseSensitivity(case_sensitivity)
def create_mail_model(parent):
model = QStandardItemModel(0, 3, parent)
- model.setHeaderData(0, Qt.Horizontal, "Subject")
- model.setHeaderData(1, Qt.Horizontal, "Sender")
- model.setHeaderData(2, Qt.Horizontal, "Date")
+ model.setHeaderData(0, Qt.Orientation.Horizontal, "Subject")
+ model.setHeaderData(1, Qt.Orientation.Horizontal, "Sender")
+ model.setHeaderData(2, Qt.Orientation.Horizontal, "Date")
add_mail(model, "Happy New Year!", "Grace K. <grace@software-inc.com>",
QDateTime(QDate(2006, 12, 31), QTime(17, 3)))
--- /dev/null
+Basic Sort/Filter Model Example
+===============================
+
+The Basic Sort/Filter Model example illustrates how to use `QSortFilterProxyModel`
+to perform basic sorting and filtering. This example demonstrates the analogous
+Qt example `Basic Sort/Filter Model Example
+<https://doc.qt.io/qt-6/qtwidgets-itemviews-basicsortfiltermodel-example.html>`_.
+
+.. image:: basicfiltermodel.webp
+ :width: 400
+ :alt: basicsortfiltermodel screenshot
# Copyright (C) 2020 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from argparse import ArgumentParser, RawTextHelpFormatter
.. image:: editabletreemodel.png
:width: 611
:alt: editabletreemodel screenshot
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from pathlib import Path
self.view = QTreeView()
self.view.setAlternatingRowColors(True)
- self.view.setSelectionBehavior(QAbstractItemView.SelectItems)
- self.view.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
+ self.view.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectItems)
+ self.view.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
self.view.setAnimated(False)
self.view.setAllColumnsShowFocus(True)
self.setCentralWidget(self.view)
for column in range(model.columnCount(index)):
child: QModelIndex = model.index(0, column, index)
- model.setData(child, "[No data]", Qt.EditRole)
- if not model.headerData(column, Qt.Horizontal):
- model.setHeaderData(column, Qt.Horizontal, "[No header]",
- Qt.EditRole)
+ model.setData(child, "[No data]", Qt.ItemDataRole.EditRole)
+ if not model.headerData(column, Qt.Orientation.Horizontal):
+ model.setHeaderData(column, Qt.Orientation.Horizontal, "[No header]",
+ Qt.ItemDataRole.EditRole)
selection_model.setCurrentIndex(
- model.index(0, 0, index), QItemSelectionModel.ClearAndSelect
+ model.index(0, 0, index), QItemSelectionModel.SelectionFlag.ClearAndSelect
)
self.update_actions()
changed: bool = model.insertColumn(column + 1)
if changed:
- model.setHeaderData(column + 1, Qt.Horizontal, "[No header]",
- Qt.EditRole)
+ model.setHeaderData(column + 1, Qt.Orientation.Horizontal, "[No header]",
+ Qt.ItemDataRole.EditRole)
self.update_actions()
for column in range(model.columnCount(parent)):
child: QModelIndex = model.index(index.row() + 1, column, parent)
- model.setData(child, "[No data]", Qt.EditRole)
+ model.setData(child, "[No data]", Qt.ItemDataRole.EditRole)
@Slot()
def remove_column(self) -> None:
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
class TreeItem:
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QModelIndex, Qt, QAbstractItemModel
if not index.isValid():
return None
- if role != Qt.DisplayRole and role != Qt.EditRole:
+ if role != Qt.ItemDataRole.DisplayRole and role != Qt.ItemDataRole.EditRole:
return None
item: TreeItem = self.get_item(index)
def flags(self, index: QModelIndex) -> Qt.ItemFlags:
if not index.isValid():
- return Qt.NoItemFlags
+ return Qt.ItemFlag.NoItemFlags
- return Qt.ItemIsEditable | QAbstractItemModel.flags(self, index)
+ return Qt.ItemFlag.ItemIsEditable | QAbstractItemModel.flags(self, index)
def get_item(self, index: QModelIndex = QModelIndex()) -> TreeItem:
if index.isValid():
return self.root_item
def headerData(self, section: int, orientation: Qt.Orientation,
- role: int = Qt.DisplayRole):
- if orientation == Qt.Horizontal and role == Qt.DisplayRole:
+ role: int = Qt.ItemDataRole.DisplayRole):
+ if orientation == Qt.Orientation.Horizontal and role == Qt.ItemDataRole.DisplayRole:
return self.root_item.data(section)
return None
return parent_item.child_count()
def setData(self, index: QModelIndex, value, role: int) -> bool:
- if role != Qt.EditRole:
+ if role != Qt.ItemDataRole.EditRole:
return False
item: TreeItem = self.get_item(index)
result: bool = item.set_data(index.column(), value)
if result:
- self.dataChanged.emit(index, index, [Qt.DisplayRole, Qt.EditRole])
+ self.dataChanged.emit(index, index,
+ [Qt.ItemDataRole.DisplayRole, Qt.ItemDataRole.EditRole])
return result
def setHeaderData(self, section: int, orientation: Qt.Orientation, value,
role: int = None) -> bool:
- if role != Qt.EditRole or orientation != Qt.Horizontal:
+ if role != Qt.ItemDataRole.EditRole or orientation != Qt.Orientation.Horizontal:
return False
result: bool = self.root_item.set_data(section, value)
--- /dev/null
+Fetch More Example
+==================
+
+The Fetch More example shows how to add items to an item view model on demand.
+This example demonstrates the analogous Qt example `Fetch More Example
+<https://doc.qt.io/qt-6/qtwidgets-itemviews-fetchmore-example.html>`_.
+
+.. image:: fetchmore.png
+ :width: 400
+ :alt: fetchmore screenshot
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the itemviews/fetchmore/fetchmore example from Qt v6.x
def rowCount(self, parent=QModelIndex()):
return self._file_count
- def data(self, index, role=Qt.DisplayRole):
+ def data(self, index, role=Qt.ItemDataRole.DisplayRole):
if not index.isValid():
return None
if row >= len(self._file_list) or row < 0:
return None
- if role == Qt.DisplayRole:
+ if role == Qt.ItemDataRole.DisplayRole:
return self._file_list[row].fileName()
- if role == Qt.BackgroundRole:
+ if role == Qt.ItemDataRole.BackgroundRole:
batch = row // BATCH_SIZE
palette = qApp.palette() # noqa: F821
return palette.base() if batch % 2 == 0 else palette.alternateBase()
- if role == Qt.DecorationRole:
+ if role == Qt.ItemDataRole.DecorationRole:
return self._icon_provider.icon(self._file_list[row])
return None
directory = QDir(path)
self.beginResetModel()
- directory_filter = QDir.AllEntries | QDir.NoDot
- self._file_list = directory.entryInfoList(directory_filter, QDir.Name)
+ directory_filter = QDir.Filter.AllEntries | QDir.Filter.NoDot
+ self._file_list = directory.entryInfoList(directory_filter, QDir.SortFlag.Name)
self._file_count = 0
self.endResetModel()
self._view.setModel(self._model)
self._log_viewer = QPlainTextEdit()
- self._log_viewer.setSizePolicy(QSizePolicy(QSizePolicy.Preferred,
- QSizePolicy.Preferred))
+ self._log_viewer.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Preferred,
+ QSizePolicy.Policy.Preferred))
self._model.number_populated.connect(self.update_log)
self._view.activated.connect(self.activated)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import json
import sys
-from typing import Any, List, Dict, Union
+from typing import Any
from PySide6.QtWidgets import QTreeView, QApplication, QHeaderView
from PySide6.QtCore import QAbstractItemModel, QModelIndex, QObject, Qt, QFileInfo
@classmethod
def load(
- cls, value: Union[List, Dict], parent: "TreeItem" = None, sort=True
+ cls, value: list | dict, parent: "TreeItem" = None, sort=True
) -> "TreeItem":
"""Create a 'root' TreeItem from a nested list or a nested dictonary
item = index.internalPointer()
- if role == Qt.DisplayRole:
+ if role == Qt.ItemDataRole.DisplayRole:
if index.column() == 0:
return item.key
if index.column() == 1:
return item.value
- elif role == Qt.EditRole:
+ elif role == Qt.ItemDataRole.EditRole:
if index.column() == 1:
return item.value
role (Qt.ItemDataRole)
"""
- if role == Qt.EditRole:
+ if role == Qt.ItemDataRole.EditRole:
if index.column() == 1:
item = index.internalPointer()
item.value = str(value)
- self.dataChanged.emit(index, index, [Qt.EditRole])
+ self.dataChanged.emit(index, index, [Qt.ItemDataRole.EditRole])
return True
For the JsonModel, it returns only data for columns (orientation = Horizontal)
"""
- if role != Qt.DisplayRole:
+ if role != Qt.ItemDataRole.DisplayRole:
return None
- if orientation == Qt.Horizontal:
+ if orientation == Qt.Orientation.Horizontal:
return self._headers[section]
def index(self, row: int, column: int, parent=QModelIndex()) -> QModelIndex:
flags = super(JsonModel, self).flags(index)
if index.column() == 1:
- return Qt.ItemIsEditable | flags
+ return Qt.ItemFlag.ItemIsEditable | flags
else:
return flags
model.load(document)
view.show()
- view.header().setSectionResizeMode(0, QHeaderView.Stretch)
+ view.header().setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch)
view.setAlternatingRowColors(True)
view.resize(500, 300)
app.exec()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
#! [2]
def setEditorData(self, editor, index):
- value = index.model().data(index, Qt.EditRole)
+ value = index.model().data(index, Qt.ItemDataRole.EditRole)
editor.setValue(value)
#! [2]
def setModelData(self, editor, model, index):
editor.interpretText()
value = editor.value()
- model.setData(index, value, Qt.EditRole)
+ model.setData(index, value, Qt.ItemDataRole.EditRole)
#! [3]
#! [4]
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
sheet = SpreadSheet(10, 6)
sheet.setWindowIcon(QPixmap(":/images/interview.png"))
sheet.show()
- sheet.layout().setSizeConstraint(QLayout.SetFixedSize)
+ sheet.layout().setSizeConstraint(QLayout.SizeConstraint.SetFixedSize)
sys.exit(app.exec())
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QPoint, Qt, QCoreApplication, Slot
from PySide6.QtGui import QAction, QBrush, QPixmap, QColor, QPainter
from spreadsheetdelegate import SpreadSheetDelegate
from spreadsheetitem import SpreadSheetItem
-from typing import Optional
from numbers import Number
class SpreadSheet(QMainWindow):
- def __init__(self, rows: Number, cols: Number, parent: Optional[QWidget] = None) -> None:
+ def __init__(self, rows: Number, cols: Number, parent: QWidget | None = None) -> None:
super().__init__(parent)
self._tool_bar = QToolBar(self)
self._cell_sum_action.triggered.connect(self.action_sum)
self._cell_add_action = QAction("&Add", self)
- self._cell_add_action.setShortcut(Qt.CTRL | Qt.Key_Plus)
+ self._cell_add_action.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_Plus)
self._cell_add_action.triggered.connect(self.action_add)
self._cell_sub_action = QAction("&Subtract", self)
- self._cell_sub_action.setShortcut(Qt.CTRL | Qt.Key_Minus)
+ self._cell_sub_action.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_Minus)
self._cell_sub_action.triggered.connect(self.action_subtract)
self._cell_mul_action = QAction("&Multiply", self)
- self._cell_mul_action.setShortcut(Qt.CTRL | Qt.Key_multiply)
+ self._cell_mul_action.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_multiply)
self._cell_mul_action.triggered.connect(self.action_multiply)
self._cell_div_action = QAction("&Divide", self)
- self._cell_div_action.setShortcut(Qt.CTRL | Qt.Key_division)
+ self._cell_div_action.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_division)
self._cell_div_action.triggered.connect(self.action_divide)
self._font_action = QAction("Font...", self)
- self._font_action.setShortcut(Qt.CTRL | Qt.Key_F)
+ self._font_action.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_F)
self._font_action.triggered.connect(self.select_font)
self._color_action = QAction(QPixmap(16, 16), "Background &Color...", self)
self._color_action.triggered.connect(self.select_color)
self._clear_action = QAction("Clear", self)
- self._clear_action.setShortcut(Qt.Key_Delete)
+ self._clear_action.setShortcut(Qt.Key.Key_Delete)
self._clear_action.triggered.connect(self.clear)
self._about_spreadsheet = QAction("About Spreadsheet", self)
@Slot(QTableWidgetItem)
def update_status(self, item: QTableWidgetItem) -> None:
if item and item == self._table.currentItem():
- self.statusBar().showMessage(str(item.data(Qt.StatusTipRole)), 1000)
+ self.statusBar().showMessage(str(item.data(Qt.ItemDataRole.StatusTipRole)), 1000)
self._cell_label.setText(
"Cell: ({})".format(
SpreadSheetItem.encode_pos(self._table.row(item), self._table.column(item))
if item != self._table.currentItem():
return
if item:
- self._formula_input.setText(str(item.data(Qt.EditRole)))
+ self._formula_input.setText(str(item.data(Qt.ItemDataRole.EditRole)))
else:
self._formula_input.clear()
if not item:
self._table.setItem(row, col, SpreadSheetItem(text))
else:
- item.setData(Qt.EditRole, text)
+ item.setData(Qt.ItemDataRole.EditRole, text)
self._table.viewport().update()
@Slot()
cell1_col_input.setCurrentIndex(c1_col)
operator_label = QLabel(opText, group)
- operator_label.setAlignment(Qt.AlignHCenter)
+ operator_label.setAlignment(Qt.AlignmentFlag.AlignHCenter)
cell2_label = QLabel(c2Text, group)
cell2_row_input = QComboBox(group)
cell2_col_input.setCurrentIndex(c2_col)
equals_label = QLabel("=", group)
- equals_label.setAlignment(Qt.AlignHCenter)
+ equals_label.setAlignment(Qt.AlignmentFlag.AlignHCenter)
out_label = QLabel(outText, group)
out_row_input = QComboBox(group)
self.addAction(self._font_action)
self.addAction(self._second_separator)
self.addAction(self._clear_action)
- self.setContextMenuPolicy(Qt.ActionsContextMenu)
+ self.setContextMenuPolicy(Qt.ContextMenuPolicy.ActionsContextMenu)
def setup_contents(self) -> None:
- title_background = QBrush(Qt.lightGray)
+ title_background = QBrush(Qt.GlobalColor.lightGray)
title_font = self._table.font()
title_font.setBold(True)
self._table.setItem(8, 2, SpreadSheetItem("1240"))
self._table.setItem(9, 2, SpreadSheetItem())
- self._table.item(9, 2).setBackground(Qt.lightGray)
+ self._table.item(9, 2).setBackground(Qt.GlobalColor.lightGray)
# column 3
self._table.setItem(0, 3, SpreadSheetItem("Currency"))
self._table.setItem(8, 3, SpreadSheetItem("USD"))
self._table.setItem(9, 3, SpreadSheetItem())
- self._table.item(9, 3).setBackground(Qt.lightGray)
+ self._table.item(9, 3).setBackground(Qt.GlobalColor.lightGray)
# column 4
self._table.setItem(0, 4, SpreadSheetItem("Ex. Rate"))
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import (QAbstractItemModel, QDate, QModelIndex, QObject,
QStringListModel, Qt, Slot)
from PySide6.QtWidgets import (QCompleter, QDateTimeEdit, QLineEdit,
QStyleOptionViewItem, QStyledItemDelegate, QWidget)
-from typing import Optional
-
class SpreadSheetDelegate(QStyledItemDelegate):
- def __init__(self, parent: Optional[QObject] = None) -> None:
+ def __init__(self, parent: QObject | None = None) -> None:
super().__init__(parent)
def create_editor(self, parent: QWidget,
# create a completer with the strings in the column as model
allStrings = QStringListModel()
for i in range(1, index.model().rowCount()):
- strItem = str(index.model().data(index.sibling(i, index.column()), Qt.EditRole))
+ strItem = str(index.model().data(index.sibling(i, index.column()),
+ Qt.ItemDataRole.EditRole))
if not allStrings.contains(strItem):
allStrings.append(strItem)
def set_editor_data(self, editor: QWidget, index: QModelIndex) -> None:
edit = QLineEdit(editor)
if edit:
- edit.setText(str(index.model().data(index, Qt.EditRole)))
+ edit.setText(str(index.model().data(index, Qt.ItemDataRole.EditRole)))
return
dateEditor = QDateTimeEdit(editor)
if dateEditor:
dateEditor.setDate(
QDate.fromString(
- str(index.model().data(index, Qt.EditRole)), "d/M/yyyy"))
+ str(index.model().data(index, Qt.ItemDataRole.EditRole)), "d/M/yyyy"))
def set_model_data(self, editor: QWidget,
model: QAbstractItemModel, index: QModelIndex) -> None:
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
-from typing import Any, Tuple
+from typing import Any
from PySide6.QtCore import QMetaType, Qt
from PySide6.QtWidgets import QTableWidget, QTableWidgetItem
return super().__init_subclass__()
def data(self, role: int) -> Any:
- if role == Qt.EditRole or role == Qt.StatusTipRole:
+ if role == Qt.ItemDataRole.EditRole or role == Qt.ItemDataRole.StatusTipRole:
return self.formula()
- if role == Qt.DisplayRole:
+ if role == Qt.ItemDataRole.DisplayRole:
return self.display()
t = str(self.display())
- if role == Qt.ForegroundRole:
+ if role == Qt.ItemDataRole.ForegroundRole:
try:
number = int(t)
- color = Qt.red if number < 0 else Qt.blue
+ color = Qt.GlobalColor.red if number < 0 else Qt.GlobalColor.blue
except ValueError:
- color = Qt.black
+ color = Qt.GlobalColor.black
return color
- if role == Qt.TextAlignmentRole:
+ if role == Qt.ItemDataRole.TextAlignmentRole:
if t and (t[0].isdigit() or t[0] == '-'):
- return int(Qt.AlignRight | Qt.AlignVCenter)
+ return int(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
return super().data(role)
return result
def formula(self) -> None:
- return str(super().data(Qt.DisplayRole))
+ return str(super().data(Qt.ItemDataRole.DisplayRole))
def compute_formula(self, formula: str, widget: QTableWidget, this) -> QMetaType.Type.QVariant:
# check if the string is actually a formula or not
return result
- def decode_pos(pos: str) -> Tuple[int, int]:
+ def decode_pos(pos: str) -> tuple[int, int]:
if (not pos):
col = -1
row = -1
# Copyright (C) 2011 Arun Srinivasan <rulfzid@gmail.com>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWidgets import QStyledItemDelegate, QStyle
# If the row is currently selected, we need to make sure we
# paint the background accordingly.
- if option.state & QStyle.State_Selected:
+ if option.state & QStyle.StateFlag.State_Selected:
# The original C++ example used option.palette.foreground() to
# get the brush for painting, but there are a couple of
# problems with that:
# Create and populate the tableWidget
table_widget = QTableWidget(4, 4)
table_widget.setItemDelegate(StarDelegate())
- table_widget.setEditTriggers(QAbstractItemView.DoubleClicked
- | QAbstractItemView.SelectedClicked)
- table_widget.setSelectionBehavior(QAbstractItemView.SelectRows)
+ table_widget.setEditTriggers(QAbstractItemView.EditTrigger.DoubleClicked
+ | QAbstractItemView.EditTrigger.SelectedClicked)
+ table_widget.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
table_widget.setHorizontalHeaderLabels(["Title", "Genre", "Artist", "Rating"])
data = [["Mass in B-Minor", "Baroque", "J.S. Bach", 5],
# Copyright (C) 2011 Arun Srinivasan <rulfzid@gmail.com>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWidgets import (QWidget)
from PySide6.QtGui import (QPainter)
# Copyright (C) 2011 Arun Srinivasan <rulfzid@gmail.com>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from math import (cos, sin, pi)
PAINTING_SCALE_FACTOR = 20
-class StarRating(object):
+class StarRating:
""" Handle the actual painting of the stars themselves. """
def __init__(self, starCount=1, maxStarCount=5):
""" Paint the stars (and/or diamonds if we're in editing mode). """
painter.save()
- painter.setRenderHint(QPainter.Antialiasing, True)
- painter.setPen(Qt.NoPen)
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
+ painter.setPen(Qt.PenStyle.NoPen)
if isEditable:
painter.setBrush(palette.highlight())
for i in range(self.MAX_STAR_COUNT):
if i < self.star_count:
- painter.drawPolygon(self._star_polygon, Qt.WindingFill)
+ painter.drawPolygon(self._star_polygon, Qt.FillRule.WindingFill)
elif isEditable:
painter.drawPolygon(self._diamond_polygon, Qt.WindingFill)
painter.translate(1.0, 0.0)
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/layouts/basiclayout example from Qt v5.x"""
big_editor.setPlainText("This widget takes up all the remaining space "
"in the top-level layout.")
- button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok
+ | QDialogButtonBox.StandardButton.Cancel)
button_box.accepted.connect(self.accept)
button_box.rejected.connect(self.reject)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/layouts/borderlayout example from Qt v5.x"""
self.add(QWidgetItem(widget), position)
def expandingDirections(self) -> Qt.Orientations:
- return Qt.Horizontal | Qt.Vertical
+ return Qt.Orientation.Horizontal | Qt.Orientation.Vertical
def hasHeightForWidth(self) -> bool:
return False
@staticmethod
def create_label(text: str):
label = QLabel(text)
- label.setFrameStyle(QFrame.Box | QFrame.Raised)
+ label.setFrameStyle(QFrame.Shape.Box | QFrame.Shadow.Raised)
return label
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/layouts/dynamiclayouts example from Qt v5.x"""
main_layout.addWidget(self._rotable_group_box, 0, 0)
main_layout.addWidget(self._options_group_box, 1, 0)
main_layout.addWidget(self._button_box, 2, 0)
- main_layout.setSizeConstraint(QLayout.SetMinimumSize)
+ main_layout.setSizeConstraint(QLayout.SizeConstraint.SetMinimumSize)
self._main_layout = main_layout
self.setLayout(self._main_layout)
self._button_box.setOrientation(orientation)
new_size_hint = self._button_box.sizeHint() + QSize(spacing, spacing)
- if orientation == Qt.Horizontal:
+ if orientation == Qt.Orientation.Horizontal:
self._main_layout.addWidget(self._button_box, 2, 0)
self.resize(self.size() + QSize(-old_size_hint.width(), new_size_hint.height()))
else:
buttons_orientation_label = QLabel("Orientation of buttons:")
buttons_orientation_combo_box = QComboBox()
- buttons_orientation_combo_box.addItem("Horizontal", Qt.Horizontal)
- buttons_orientation_combo_box.addItem("Vertical", Qt.Vertical)
+ buttons_orientation_combo_box.addItem("Horizontal", Qt.Orientation.Horizontal)
+ buttons_orientation_combo_box.addItem("Vertical", Qt.Orientation.Vertical)
buttons_orientation_combo_box.currentIndexChanged[int].connect(
self.buttons_orientation_changed)
def create_button_box(self):
self._button_box = QDialogButtonBox()
- close_button = self._button_box.addButton(QDialogButtonBox.Close)
- help_button = self._button_box.addButton(QDialogButtonBox.Help)
+ close_button = self._button_box.addButton(QDialogButtonBox.StandardButton.Close)
+ help_button = self._button_box.addButton(QDialogButtonBox.StandardButton.Help)
rotate_widgets_button = self._button_box.addButton(
- "Rotate &Widgets", QDialogButtonBox.ActionRole)
+ "Rotate &Widgets", QDialogButtonBox.ButtonRole.ActionRole)
rotate_widgets_button.clicked.connect(self.rotate_widgets)
close_button.clicked.connect(self.close)
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/layouts/flowlayout example from Qt v6.x"""
for item in self._item_list:
style = item.widget().style()
layout_spacing_x = style.layoutSpacing(
- QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Horizontal
+ QSizePolicy.ControlType.PushButton, QSizePolicy.ControlType.PushButton,
+ Qt.Orientation.Horizontal
)
layout_spacing_y = style.layoutSpacing(
- QSizePolicy.PushButton, QSizePolicy.PushButton, Qt.Vertical
+ QSizePolicy.ControlType.PushButton, QSizePolicy.ControlType.PushButton,
+ Qt.Orientation.Vertical
)
space_x = spacing + layout_spacing_x
space_y = spacing + layout_spacing_y
-.. _qt-linguist-example:
-
Qt Linguist Example
===================
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
about_qt_action.triggered.connect(qApp.aboutQt) # noqa: F821
self._list_widget = QListWidget()
- self._list_widget.setSelectionMode(QAbstractItemView.MultiSelection)
+ self._list_widget.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection)
self._list_widget.selectionModel().selectionChanged.connect(self.selection_changed)
self._list_widget.addItem("C++")
self._list_widget.addItem("Java")
if __name__ == '__main__':
app = QApplication(sys.argv)
- path = QLibraryInfo.path(QLibraryInfo.TranslationsPath)
+ path = QLibraryInfo.path(QLibraryInfo.LibraryPath.TranslationsPath)
translator = QTranslator(app)
if translator.load(QLocale.system(), 'qtbase', '_', path):
app.installTranslator(translator)
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from argparse import ArgumentParser, RawTextHelpFormatter
import sys
def create_actions(self):
icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentNew, QIcon(':/images/new.png'))
- self._new_act = QAction(icon, "&New", self, shortcut=QKeySequence.New,
+ self._new_act = QAction(icon, "&New", self, shortcut=QKeySequence.StandardKey.New,
statusTip="Create a new file", triggered=self.new_file)
icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentOpen, QIcon(':/images/open.png'))
self._open_act = QAction(icon, "&Open...", self,
- shortcut=QKeySequence.Open, statusTip="Open an existing file",
+ shortcut=QKeySequence.StandardKey.Open,
+ statusTip="Open an existing file",
triggered=self.open)
icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentSave, QIcon(':/images/save.png'))
self._save_act = QAction(icon, "&Save", self,
- shortcut=QKeySequence.Save,
+ shortcut=QKeySequence.StandardKey.Save,
statusTip="Save the document to disk", triggered=self.save)
self._save_as_act = QAction("Save &As...", self,
- shortcut=QKeySequence.SaveAs,
+ shortcut=QKeySequence.StandardKey.SaveAs,
statusTip="Save the document under a new name",
triggered=self.save_as)
statusTip="Exit the application", triggered=self.close)
icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCut, QIcon(':/images/cut.png'))
- self._cut_act = QAction(icon, "Cu&t", self, shortcut=QKeySequence.Cut,
+ self._cut_act = QAction(icon, "Cu&t", self, shortcut=QKeySequence.StandardKey.Cut,
statusTip="Cut the current selection's contents to the clipboard",
triggered=self._text_edit.cut)
icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCopy, QIcon(':/images/copy.png'))
self._copy_act = QAction(icon, "&Copy",
- self, shortcut=QKeySequence.Copy,
+ self, shortcut=QKeySequence.StandardKey.Copy,
statusTip="Copy the current selection's contents to the clipboard",
triggered=self._text_edit.copy)
icon = QIcon.fromTheme(QIcon.ThemeIcon.EditPaste, QIcon(':/images/paste.png'))
self._paste_act = QAction(icon, "&Paste",
- self, shortcut=QKeySequence.Paste,
+ self, shortcut=QKeySequence.StandardKey.Paste,
statusTip="Paste the clipboard's contents into the current "
"selection",
triggered=self._text_edit.paste)
ret = QMessageBox.warning(self, "Application",
"The document has been modified.\nDo you want to save "
"your changes?",
- QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
- if ret == QMessageBox.Save:
+ QMessageBox.StandardButton.Save
+ | QMessageBox.StandardButton.Discard
+ | QMessageBox.StandardButton.Cancel)
+ if ret == QMessageBox.StandardButton.Save:
return self.save()
- elif ret == QMessageBox.Cancel:
+ elif ret == QMessageBox.StandardButton.Cancel:
return False
return True
def load_file(self, fileName):
file = QFile(fileName)
- if not file.open(QFile.ReadOnly | QFile.Text):
+ if not file.open(QFile.OpenModeFlag.ReadOnly | QFile.OpenModeFlag.Text):
reason = file.errorString()
QMessageBox.warning(self, "Application", f"Cannot read file {fileName}:\n{reason}.")
return
inf = QTextStream(file)
- with QApplication.setOverrideCursor(Qt.WaitCursor):
+ with QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor):
self._text_edit.setPlainText(inf.readAll())
self.set_current_file(fileName)
def save_file(self, fileName):
error = None
- with QApplication.setOverrideCursor(Qt.WaitCursor):
+ with QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor):
file = QSaveFile(fileName)
- if file.open(QFile.WriteOnly | QFile.Text):
+ if file.open(QFile.OpenModeFlag.WriteOnly | QFile.OpenModeFlag.Text):
outf = QTextStream(file)
outf << self._text_edit.toPlainText()
if not file.commit():
--- /dev/null
+Application Example
+===================
+
+This application is a simple text editor built using PySide6, demonstrating
+how to create a modern GUI application with a menu bar, toolbars, and a status
+bar. It supports basic file operations such as creating, opening, saving, and
+editing text files. It demonstrates the analogous Qt example `Application Example
+<https://doc.qt.io/qt-6.2/qtwidgets-mainwindows-application-example.html>`_.
+
+.. image:: application.png
+ :width: 400
+ :alt: application screenshot
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/mainwindows/dockwidgets example from Qt v5.x,
originating from PyQt"""
self._text_edit.clear()
cursor = self._text_edit.textCursor()
- cursor.movePosition(QTextCursor.Start)
+ cursor.movePosition(QTextCursor.MoveOperation.Start)
top_frame = cursor.currentFrame()
top_frame_format = top_frame.frameFormat()
top_frame_format.setPadding(16)
text_format = QTextCharFormat()
bold_format = QTextCharFormat()
- bold_format.setFontWeight(QFont.Bold)
+ bold_format.setFontWeight(QFont.Weight.Bold)
italic_format = QTextCharFormat()
italic_format.setFontItalic(True)
table_format = QTextTableFormat()
table_format.setBorder(1)
table_format.setCellPadding(16)
- table_format.setAlignment(Qt.AlignRight)
+ table_format.setAlignment(Qt.AlignmentFlag.AlignRight)
cursor.insertTable(1, 1, table_format)
cursor.insertText("The Firm", bold_format)
cursor.insertBlock()
printer = QPrinter()
dlg = QPrintDialog(printer, self)
- if dlg.exec() != QDialog.Accepted:
+ if dlg.exec() != QDialog.DialogCode.Accepted:
return
document.print_(printer)
def save(self):
dialog = QFileDialog(self, "Choose a file name")
dialog.setMimeTypeFilters(['text/html'])
- dialog.setAcceptMode(QFileDialog.AcceptSave)
+ dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
dialog.setDefaultSuffix('html')
- if dialog.exec() != QDialog.Accepted:
+ if dialog.exec() != QDialog.DialogCode.Accepted:
return
filename = dialog.selectedFiles()[0]
file = QFile(filename)
- if not file.open(QFile.WriteOnly | QFile.Text):
+ if not file.open(QFile.OpenModeFlag.WriteOnly | QFile.OpenModeFlag.Text):
reason = file.errorString()
QMessageBox.warning(self, "Dock Widgets",
f"Cannot write file {filename}:\n{reason}.")
return
out = QTextStream(file)
- with QApplication.setOverrideCursor(Qt.WaitCursor):
+ with QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor):
out << self._text_edit.toHtml()
self.statusBar().showMessage(f"Saved '{filename}'", 2000)
if cursor.isNull():
return
cursor.beginEditBlock()
- cursor.movePosition(QTextCursor.PreviousBlock,
- QTextCursor.MoveAnchor, 2)
+ cursor.movePosition(QTextCursor.MoveOperation.PreviousBlock,
+ QTextCursor.MoveMode.MoveAnchor, 2)
cursor.insertBlock()
cursor.insertText(paragraph)
cursor.insertBlock()
def create_actions(self):
icon = QIcon.fromTheme('document-new', QIcon(':/images/new.png'))
self._new_letter_act = QAction(icon, "&New Letter",
- self, shortcut=QKeySequence.New,
+ self, shortcut=QKeySequence.StandardKey.New,
statusTip="Create a new form letter",
triggered=self.new_letter)
icon = QIcon.fromTheme('document-save', QIcon(':/images/save.png'))
self._save_act = QAction(icon, "&Save...", self,
- shortcut=QKeySequence.Save,
+ shortcut=QKeySequence.StandardKey.Save,
statusTip="Save the current form letter", triggered=self.save)
icon = QIcon.fromTheme('document-print', QIcon(':/images/print.png'))
self._print_act = QAction(icon, "&Print...", self,
- shortcut=QKeySequence.Print,
+ shortcut=QKeySequence.StandardKey.Print,
statusTip="Print the current form letter",
triggered=self.print_)
icon = QIcon.fromTheme('edit-undo', QIcon(':/images/undo.png'))
self._undo_act = QAction(icon, "&Undo", self,
- shortcut=QKeySequence.Undo,
+ shortcut=QKeySequence.StandardKey.Undo,
statusTip="Undo the last editing action", triggered=self.undo)
self._quit_act = QAction("&Quit", self, shortcut="Ctrl+Q",
def create_dock_windows(self):
dock = QDockWidget("Customers", self)
- dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
+ dock.setAllowedAreas(Qt.DockWidgetArea.LeftDockWidgetArea
+ | Qt.DockWidgetArea.RightDockWidgetArea)
self._customer_list = QListWidget(dock)
self._customer_list.addItems((
"John Doe, Harmony Enterprises, 12 Lakeside, Ambleton",
"Sol Harvey, Chicos Coffee, 53 New Springs, Eccleston",
"Sally Hobart, Tiroli Tea, 67 Long River, Fedula"))
dock.setWidget(self._customer_list)
- self.addDockWidget(Qt.RightDockWidgetArea, dock)
+ self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, dock)
self._view_menu.addAction(dock.toggleViewAction())
dock = QDockWidget("Paragraphs", self)
"You made an overpayment (more than $5). Do you wish to buy more "
"items, or should we return the excess to you?"))
dock.setWidget(self._paragraphs_list)
- self.addDockWidget(Qt.RightDockWidgetArea, dock)
+ self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, dock)
self._view_menu.addAction(dock.toggleViewAction())
self._customer_list.currentTextChanged.connect(self.insert_customer)
--- /dev/null
+MDI Example
+===========
+
+The MDI example shows how to implement a Multiple Document Interface using
+Qt's `QMdiArea` class.
+
+.. image:: mdi.png
+ :width: 400
+ :alt: mdi screenshot
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/mainwindows/mdi example from Qt v5.x, originating from PyQt"""
def __init__(self):
super().__init__()
- self.setAttribute(Qt.WA_DeleteOnClose)
+ self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
self._is_untitled = True
def new_file(self):
def load_file(self, fileName):
file = QFile(fileName)
- if not file.open(QFile.ReadOnly | QFile.Text):
+ if not file.open(QFile.OpenModeFlag.ReadOnly | QFile.OpenModeFlag.Text):
reason = file.errorString()
message = f"Cannot read file {fileName}:\n{reason}."
QMessageBox.warning(self, "MDI", message)
return False
instr = QTextStream(file)
- with QApplication.setOverrideCursor(Qt.WaitCursor):
+ with QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor):
self.setPlainText(instr.readAll())
self.set_current_file(fileName)
def save_file(self, fileName):
error = None
- with QApplication.setOverrideCursor(Qt.WaitCursor):
+ with QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor):
file = QSaveFile(fileName)
- if file.open(QFile.WriteOnly | QFile.Text):
+ if file.open(QFile.OpenModeFlag.WriteOnly | QFile.OpenModeFlag.Text):
outstr = QTextStream(file)
outstr << self.toPlainText()
if not file.commit():
super().__init__()
self._mdi_area = QMdiArea()
- self._mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
- self._mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
+ self._mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
+ self._mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
self.setCentralWidget(self._mdi_area)
self._mdi_area.subWindowActivated.connect(self.update_menus)
def create_actions(self):
icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentNew)
- self._new_act = QAction(icon, "&New", self,
- shortcut=QKeySequence.New, statusTip="Create a new file",
- triggered=self.new_file)
+ self._new_act = QAction(icon, "&New", self, shortcut=QKeySequence.StandardKey.New,
+ statusTip="Create a new file", triggered=self.new_file)
icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentOpen)
- self._open_act = QAction(icon, "&Open...", self,
- shortcut=QKeySequence.Open, statusTip="Open an existing file",
- triggered=self.open)
+ self._open_act = QAction(icon, "&Open...", self, shortcut=QKeySequence.StandardKey.Open,
+ statusTip="Open an existing file", triggered=self.open)
icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentSave)
self._save_act = QAction(icon, "&Save", self,
- shortcut=QKeySequence.Save,
+ shortcut=QKeySequence.StandardKey.Save,
statusTip="Save the document to disk", triggered=self.save)
self._save_as_act = QAction("Save &As...", self,
- shortcut=QKeySequence.SaveAs,
+ shortcut=QKeySequence.StandardKey.SaveAs,
statusTip="Save the document under a new name",
triggered=self.save_as)
icon = QIcon.fromTheme(QIcon.ThemeIcon.ApplicationExit)
- self._exit_act = QAction(icon, "E&xit", self, shortcut=QKeySequence.Quit,
+ self._exit_act = QAction(icon, "E&xit", self, shortcut=QKeySequence.StandardKey.Quit,
statusTip="Exit the application",
triggered=QApplication.instance().closeAllWindows)
icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCut)
self._cut_act = QAction(icon, "Cu&t", self,
- shortcut=QKeySequence.Cut,
+ shortcut=QKeySequence.StandardKey.Cut,
statusTip="Cut the current selection's contents to the clipboard",
triggered=self.cut)
icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCopy)
self._copy_act = QAction(icon, "&Copy", self,
- shortcut=QKeySequence.Copy,
+ shortcut=QKeySequence.StandardKey.Copy,
statusTip="Copy the current selection's contents to the clipboard",
triggered=self.copy)
icon = QIcon.fromTheme(QIcon.ThemeIcon.EditPaste)
self._paste_act = QAction(icon, "&Paste", self,
- shortcut=QKeySequence.Paste,
+ shortcut=QKeySequence.StandardKey.Paste,
statusTip="Paste the clipboard's contents into the current "
"selection",
triggered=self.paste)
statusTip="Cascade the windows",
triggered=self._mdi_area.cascadeSubWindows)
- self._next_act = QAction("Ne&xt", self, shortcut=QKeySequence.NextChild,
+ self._next_act = QAction("Ne&xt", self, shortcut=QKeySequence.StandardKey.NextChild,
statusTip="Move the focus to the next window",
triggered=self._mdi_area.activateNextSubWindow)
self._previous_act = QAction("Pre&vious", self,
- shortcut=QKeySequence.PreviousChild,
+ shortcut=QKeySequence.StandardKey.PreviousChild,
statusTip="Move the focus to the previous window",
triggered=self._mdi_area.activatePreviousSubWindow)
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/painting/basicdrawing example from Qt v5.x, originating from PyQt"""
self.transformed = False
self.pixmap.load(':/images/qt-logo.png')
- self.setBackgroundRole(QPalette.Base)
+ self.setBackgroundRole(QPalette.ColorRole.Base)
self.setAutoFillBackground(True)
def minimumSizeHint(self):
painter.setPen(self.pen)
painter.setBrush(self.brush)
if self.antialiased:
- painter.setRenderHint(QPainter.Antialiasing)
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
for x in range(0, self.width(), 100):
for y in range(0, self.height(), 100):
elif self.shape == RenderArea.Rect:
painter.drawRect(rect)
elif self.shape == RenderArea.RoundedRect:
- painter.drawRoundedRect(rect, 25, 25, Qt.RelativeSize)
+ painter.drawRoundedRect(rect, 25, 25, Qt.SizeMode.RelativeSize)
elif self.shape == RenderArea.Ellipse:
painter.drawEllipse(rect)
elif self.shape == RenderArea.Arc:
painter.drawPath(path)
elif self.shape == RenderArea.Text:
qv = qVersion()
- painter.drawText(rect, Qt.AlignCenter,
+ painter.drawText(rect, Qt.AlignmentFlag.AlignCenter,
f"PySide 6\nQt {qv}")
elif self.shape == RenderArea.Pixmap:
painter.drawPixmap(10, 10, self.pixmap)
painter.restore()
painter.setPen(self.palette().dark().color())
- painter.setBrush(Qt.NoBrush)
+ painter.setBrush(Qt.BrushStyle.NoBrush)
painter.drawRect(QRect(0, 0, self.width() - 1, self.height() - 1))
-id_role = Qt.UserRole
+id_role = Qt.ItemDataRole.UserRole
class Window(QWidget):
pen_width_label.setBuddy(self._pen_width_spin_box)
self._pen_style_combo_box = QComboBox()
- self._pen_style_combo_box.addItem("Solid", Qt.SolidLine)
- self._pen_style_combo_box.addItem("Dash", Qt.DashLine)
- self._pen_style_combo_box.addItem("Dot", Qt.DotLine)
- self._pen_style_combo_box.addItem("Dash Dot", Qt.DashDotLine)
- self._pen_style_combo_box.addItem("Dash Dot Dot", Qt.DashDotDotLine)
- self._pen_style_combo_box.addItem("None", Qt.NoPen)
+ self._pen_style_combo_box.addItem("Solid", Qt.PenStyle.SolidLine)
+ self._pen_style_combo_box.addItem("Dash", Qt.PenStyle.DashLine)
+ self._pen_style_combo_box.addItem("Dot", Qt.PenStyle.DotLine)
+ self._pen_style_combo_box.addItem("Dash Dot", Qt.PenStyle.DashDotLine)
+ self._pen_style_combo_box.addItem("Dash Dot Dot", Qt.PenStyle.DashDotDotLine)
+ self._pen_style_combo_box.addItem("None", Qt.PenStyle.NoPen)
pen_style_label = QLabel("&Pen Style:")
pen_style_label.setBuddy(self._pen_style_combo_box)
self._pen_cap_combo_box = QComboBox()
- self._pen_cap_combo_box.addItem("Flat", Qt.FlatCap)
- self._pen_cap_combo_box.addItem("Square", Qt.SquareCap)
- self._pen_cap_combo_box.addItem("Round", Qt.RoundCap)
+ self._pen_cap_combo_box.addItem("Flat", Qt.PenCapStyle.FlatCap)
+ self._pen_cap_combo_box.addItem("Square", Qt.PenCapStyle.SquareCap)
+ self._pen_cap_combo_box.addItem("Round", Qt.PenCapStyle.RoundCap)
pen_cap_label = QLabel("Pen &Cap:")
pen_cap_label.setBuddy(self._pen_cap_combo_box)
self._pen_join_combo_box = QComboBox()
- self._pen_join_combo_box.addItem("Miter", Qt.MiterJoin)
- self._pen_join_combo_box.addItem("Bevel", Qt.BevelJoin)
- self._pen_join_combo_box.addItem("Round", Qt.RoundJoin)
+ self._pen_join_combo_box.addItem("Miter", Qt.PenJoinStyle.MiterJoin)
+ self._pen_join_combo_box.addItem("Bevel", Qt.PenJoinStyle.BevelJoin)
+ self._pen_join_combo_box.addItem("Round", Qt.PenJoinStyle.RoundJoin)
pen_join_label = QLabel("Pen &Join:")
pen_join_label.setBuddy(self._pen_join_combo_box)
self._brush_style_combo_box = QComboBox()
- self._brush_style_combo_box.addItem("Linear Gradient", Qt.LinearGradientPattern)
- self._brush_style_combo_box.addItem("Radial Gradient", Qt.RadialGradientPattern)
- self._brush_style_combo_box.addItem("Conical Gradient", Qt.ConicalGradientPattern)
- self._brush_style_combo_box.addItem("Texture", Qt.TexturePattern)
- self._brush_style_combo_box.addItem("Solid", Qt.SolidPattern)
- self._brush_style_combo_box.addItem("Horizontal", Qt.HorPattern)
- self._brush_style_combo_box.addItem("Vertical", Qt.VerPattern)
- self._brush_style_combo_box.addItem("Cross", Qt.CrossPattern)
- self._brush_style_combo_box.addItem("Backward Diagonal", Qt.BDiagPattern)
- self._brush_style_combo_box.addItem("Forward Diagonal", Qt.FDiagPattern)
- self._brush_style_combo_box.addItem("Diagonal Cross", Qt.DiagCrossPattern)
- self._brush_style_combo_box.addItem("Dense 1", Qt.Dense1Pattern)
- self._brush_style_combo_box.addItem("Dense 2", Qt.Dense2Pattern)
- self._brush_style_combo_box.addItem("Dense 3", Qt.Dense3Pattern)
- self._brush_style_combo_box.addItem("Dense 4", Qt.Dense4Pattern)
- self._brush_style_combo_box.addItem("Dense 5", Qt.Dense5Pattern)
- self._brush_style_combo_box.addItem("Dense 6", Qt.Dense6Pattern)
- self._brush_style_combo_box.addItem("Dense 7", Qt.Dense7Pattern)
- self._brush_style_combo_box.addItem("None", Qt.NoBrush)
+ self._brush_style_combo_box.addItem("Linear Gradient", Qt.BrushStyle.LinearGradientPattern)
+ self._brush_style_combo_box.addItem("Radial Gradient", Qt.BrushStyle.RadialGradientPattern)
+ self._brush_style_combo_box.addItem("Conical Gradient",
+ Qt.BrushStyle.ConicalGradientPattern)
+ self._brush_style_combo_box.addItem("Texture", Qt.BrushStyle.TexturePattern)
+ self._brush_style_combo_box.addItem("Solid", Qt.BrushStyle.SolidPattern)
+ self._brush_style_combo_box.addItem("Horizontal", Qt.BrushStyle.HorPattern)
+ self._brush_style_combo_box.addItem("Vertical", Qt.BrushStyle.VerPattern)
+ self._brush_style_combo_box.addItem("Cross", Qt.BrushStyle.CrossPattern)
+ self._brush_style_combo_box.addItem("Backward Diagonal", Qt.BrushStyle.BDiagPattern)
+ self._brush_style_combo_box.addItem("Forward Diagonal", Qt.BrushStyle.FDiagPattern)
+ self._brush_style_combo_box.addItem("Diagonal Cross", Qt.BrushStyle.DiagCrossPattern)
+ self._brush_style_combo_box.addItem("Dense 1", Qt.BrushStyle.Dense1Pattern)
+ self._brush_style_combo_box.addItem("Dense 2", Qt.BrushStyle.Dense2Pattern)
+ self._brush_style_combo_box.addItem("Dense 3", Qt.BrushStyle.Dense3Pattern)
+ self._brush_style_combo_box.addItem("Dense 4", Qt.BrushStyle.Dense4Pattern)
+ self._brush_style_combo_box.addItem("Dense 5", Qt.BrushStyle.Dense5Pattern)
+ self._brush_style_combo_box.addItem("Dense 6", Qt.BrushStyle.Dense6Pattern)
+ self._brush_style_combo_box.addItem("Dense 7", Qt.BrushStyle.Dense7Pattern)
+ self._brush_style_combo_box.addItem("None", Qt.BrushStyle.NoBrush)
brush_style_label = QLabel("&Brush Style:")
brush_style_label.setBuddy(self._brush_style_combo_box)
main_layout.setColumnStretch(3, 1)
main_layout.addWidget(self._render_area, 0, 0, 1, 4)
main_layout.setRowMinimumHeight(1, 6)
- main_layout.addWidget(shape_label, 2, 1, Qt.AlignRight)
+ main_layout.addWidget(shape_label, 2, 1, Qt.AlignmentFlag.AlignRight)
main_layout.addWidget(self._shape_combo_box, 2, 2)
- main_layout.addWidget(pen_width_label, 3, 1, Qt.AlignRight)
+ main_layout.addWidget(pen_width_label, 3, 1, Qt.AlignmentFlag.AlignRight)
main_layout.addWidget(self._pen_width_spin_box, 3, 2)
- main_layout.addWidget(pen_style_label, 4, 1, Qt.AlignRight)
+ main_layout.addWidget(pen_style_label, 4, 1, Qt.AlignmentFlag.AlignRight)
main_layout.addWidget(self._pen_style_combo_box, 4, 2)
- main_layout.addWidget(pen_cap_label, 5, 1, Qt.AlignRight)
+ main_layout.addWidget(pen_cap_label, 5, 1, Qt.AlignmentFlag.AlignRight)
main_layout.addWidget(self._pen_cap_combo_box, 5, 2)
- main_layout.addWidget(pen_join_label, 6, 1, Qt.AlignRight)
+ main_layout.addWidget(pen_join_label, 6, 1, Qt.AlignmentFlag.AlignRight)
main_layout.addWidget(self._pen_join_combo_box, 6, 2)
- main_layout.addWidget(brush_style_label, 7, 1, Qt.AlignRight)
+ main_layout.addWidget(brush_style_label, 7, 1, Qt.AlignmentFlag.AlignRight)
main_layout.addWidget(self._brush_style_combo_box, 7, 2)
main_layout.setRowMinimumHeight(8, 6)
- main_layout.addWidget(other_options_label, 9, 1, Qt.AlignRight)
+ main_layout.addWidget(other_options_label, 9, 1, Qt.AlignmentFlag.AlignRight)
main_layout.addWidget(self._antialiasing_check_box, 9, 2)
main_layout.addWidget(self._transformations_check_box, 10, 2)
self.setLayout(main_layout)
join = Qt.PenJoinStyle(self._pen_join_combo_box.itemData(
self._pen_join_combo_box.currentIndex(), id_role))
- self._render_area.set_pen(QPen(Qt.blue, width, style, cap, join))
+ self._render_area.set_pen(QPen(Qt.GlobalColor.blue, width, style, cap, join))
def brush_changed(self):
style = Qt.BrushStyle(self._brush_style_combo_box.itemData(
self._brush_style_combo_box.currentIndex(), id_role))
- if style == Qt.LinearGradientPattern:
+ if style == Qt.BrushStyle.LinearGradientPattern:
linear_gradient = QLinearGradient(0, 0, 100, 100)
- linear_gradient.setColorAt(0.0, Qt.white)
- linear_gradient.setColorAt(0.2, Qt.green)
- linear_gradient.setColorAt(1.0, Qt.black)
+ linear_gradient.setColorAt(0.0, Qt.GlobalColor.white)
+ linear_gradient.setColorAt(0.2, Qt.GlobalColor.green)
+ linear_gradient.setColorAt(1.0, Qt.GlobalColor.black)
self._render_area.set_brush(QBrush(linear_gradient))
- elif style == Qt.RadialGradientPattern:
+ elif style == Qt.BrushStyle.RadialGradientPattern:
radial_gradient = QRadialGradient(50, 50, 50, 70, 70)
- radial_gradient.setColorAt(0.0, Qt.white)
- radial_gradient.setColorAt(0.2, Qt.green)
- radial_gradient.setColorAt(1.0, Qt.black)
+ radial_gradient.setColorAt(0.0, Qt.GlobalColor.white)
+ radial_gradient.setColorAt(0.2, Qt.GlobalColor.green)
+ radial_gradient.setColorAt(1.0, Qt.GlobalColor.black)
self._render_area.set_brush(QBrush(radial_gradient))
- elif style == Qt.ConicalGradientPattern:
+ elif style == Qt.BrushStyle.ConicalGradientPattern:
conical_gradient = QConicalGradient(50, 50, 150)
- conical_gradient.setColorAt(0.0, Qt.white)
- conical_gradient.setColorAt(0.2, Qt.green)
- conical_gradient.setColorAt(1.0, Qt.black)
+ conical_gradient.setColorAt(0.0, Qt.GlobalColor.white)
+ conical_gradient.setColorAt(0.2, Qt.GlobalColor.green)
+ conical_gradient.setColorAt(1.0, Qt.GlobalColor.black)
self._render_area.set_brush(QBrush(conical_gradient))
- elif style == Qt.TexturePattern:
+ elif style == Qt.BrushStyle.TexturePattern:
self._render_area.set_brush(QBrush(QPixmap(':/images/brick.png')))
else:
- self._render_area.set_brush(QBrush(Qt.green, style))
+ self._render_area.set_brush(QBrush(Qt.GlobalColor.green, style))
if __name__ == '__main__':
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/painting/concentriccircles example from Qt v5.x, originating
from PyQt"""
self.antialiased = False
self._frame_no = 0
- self.setBackgroundRole(QPalette.Base)
- self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ self.setBackgroundRole(QPalette.ColorRole.Base)
+ self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
def set_float_based(self, floatBased):
self._float_based = floatBased
def paintEvent(self, event):
with QPainter(self) as painter:
- painter.setRenderHint(QPainter.Antialiasing, self.antialiased)
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing, self.antialiased)
painter.translate(self.width() / 2, self.height() / 2)
for diameter in range(0, 256, 9):
def create_label(self, text):
label = QLabel(text)
- label.setAlignment(Qt.AlignCenter)
+ label.setAlignment(Qt.AlignmentFlag.AlignCenter)
label.setMargin(2)
- label.setFrameStyle(QFrame.Box | QFrame.Sunken)
+ label.setFrameStyle(QFrame.Shape.Box | QFrame.Shadow.Sunken)
return label
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWidgets import (
QWidget,
self.setFixedSize(680, 480)
self.pixmap = QPixmap(self.size())
- self.pixmap.fill(Qt.white)
+ self.pixmap.fill(Qt.GlobalColor.white)
self.previous_pos = None
self.painter = QPainter()
self.pen = QPen()
self.pen.setWidth(10)
- self.pen.setCapStyle(Qt.RoundCap)
- self.pen.setJoinStyle(Qt.RoundJoin)
+ self.pen.setCapStyle(Qt.PenCapStyle.RoundCap)
+ self.pen.setJoinStyle(Qt.PenJoinStyle.RoundJoin)
def paintEvent(self, event: QPaintEvent):
"""Override method from QWidget
"""
current_pos = event.position().toPoint()
self.painter.begin(self.pixmap)
- self.painter.setRenderHints(QPainter.Antialiasing, True)
+ self.painter.setRenderHints(QPainter.RenderHint.Antialiasing, True)
self.painter.setPen(self.pen)
self.painter.drawLine(self.previous_pos, current_pos)
self.painter.end()
def load(self, filename: str):
""" load pixmap from filename """
self.pixmap.load(filename)
- self.pixmap = self.pixmap.scaled(self.size(), Qt.KeepAspectRatio)
+ self.pixmap = self.pixmap.scaled(self.size(), Qt.AspectRatioMode.KeepAspectRatio)
self.update()
def clear(self):
""" Clear the pixmap """
- self.pixmap.fill(Qt.white)
+ self.pixmap.fill(Qt.GlobalColor.white)
self.update()
self.painter_widget = PainterWidget()
self.bar = self.addToolBar("Menu")
- self.bar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
+ self.bar.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
self._save_action = self.bar.addAction(
- qApp.style().standardIcon(QStyle.SP_DialogSaveButton), # noqa: F821
+ qApp.style().standardIcon(QStyle.StandardPixmap.SP_DialogSaveButton), # noqa: F821
"Save", self.on_save
)
- self._save_action.setShortcut(QKeySequence.Save)
+ self._save_action.setShortcut(QKeySequence.StandardKey.Save)
self._open_action = self.bar.addAction(
- qApp.style().standardIcon(QStyle.SP_DialogOpenButton), # noqa: F821
+ qApp.style().standardIcon(QStyle.StandardPixmap.SP_DialogOpenButton), # noqa: F821
"Open", self.on_open
)
- self._open_action.setShortcut(QKeySequence.Open)
+ self._open_action.setShortcut(QKeySequence.StandardKey.Open)
self.bar.addAction(
- qApp.style().standardIcon(QStyle.SP_DialogResetButton), # noqa: F821
+ qApp.style().standardIcon(QStyle.StandardPixmap.SP_DialogResetButton), # noqa: F821
"Clear",
self.painter_widget.clear,
)
self.setCentralWidget(self.painter_widget)
- self.color = Qt.black
+ self.color = Qt.GlobalColor.black
self.set_color(self.color)
self.mime_type_filters = ["image/png", "image/jpeg"]
dialog = QFileDialog(self, "Save File")
dialog.setMimeTypeFilters(self.mime_type_filters)
- dialog.setFileMode(QFileDialog.AnyFile)
- dialog.setAcceptMode(QFileDialog.AcceptSave)
+ dialog.setFileMode(QFileDialog.FileMode.AnyFile)
+ dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptSave)
dialog.setDefaultSuffix("png")
dialog.setDirectory(
- QStandardPaths.writableLocation(QStandardPaths.PicturesLocation)
+ QStandardPaths.writableLocation(QStandardPaths.StandardLocation.PicturesLocation)
)
- if dialog.exec() == QFileDialog.Accepted:
+ if dialog.exec() == QFileDialog.DialogCode.Accepted:
if dialog.selectedFiles():
self.painter_widget.save(dialog.selectedFiles()[0])
dialog = QFileDialog(self, "Save File")
dialog.setMimeTypeFilters(self.mime_type_filters)
- dialog.setFileMode(QFileDialog.ExistingFile)
- dialog.setAcceptMode(QFileDialog.AcceptOpen)
+ dialog.setFileMode(QFileDialog.FileMode.ExistingFile)
+ dialog.setAcceptMode(QFileDialog.AcceptMode.AcceptOpen)
dialog.setDefaultSuffix("png")
dialog.setDirectory(
- QStandardPaths.writableLocation(QStandardPaths.PicturesLocation)
+ QStandardPaths.writableLocation(QStandardPaths.StandardLocation.PicturesLocation)
)
- if dialog.exec() == QFileDialog.Accepted:
+ if dialog.exec() == QFileDialog.DialogCode.Accepted:
if dialog.selectedFiles():
self.painter_widget.load(dialog.selectedFiles()[0])
if color:
self.set_color(color)
- def set_color(self, color: QColor = Qt.black):
+ def set_color(self, color: QColor = Qt.GlobalColor.black):
self.color = color
# Create color icon
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import math
import sys
def paintEvent(self, event):
with QPainter(self) as painter:
rect = QRect(QPoint(0, 0), self.size())
- painter.fillRect(rect, Qt.white)
+ painter.fillRect(rect, Qt.GlobalColor.white)
painter.translate(-self._points[0].x(), 0)
painter.drawPolyline(self._points)
-.. _rhi-widget-example:
-
Simple RHI Widget Example
=========================
Shows how to render a triangle using ``QRhi``, Qt's 3D API and shading
language abstraction layer.
-This example is, in many ways, the counterpart of the :ref:`rhi-window-example`
+This example is, in many ways, the counterpart of the :ref:`example_gui_rhiwindow`
in the QWidget world. The ``QRhiWidget`` subclass in this applications renders
a single triangle, using a simple graphics pipeline with basic vertex and
fragment shaders. Unlike the plain ``QWindow``-based application, this example
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import numpy
def getShader(name):
f = QFile(name)
- if f.open(QIODevice.ReadOnly):
+ if f.open(QIODevice.OpenModeFlag.ReadOnly):
return QShader.fromSerialized(f.readAll())
return QShader()
if not self.m_pipeline:
vertex_size = 4 * VERTEX_DATA.size
- self.m_vbuf = self.m_rhi.newBuffer(QRhiBuffer.Immutable,
- QRhiBuffer.VertexBuffer, vertex_size)
+ self.m_vbuf = self.m_rhi.newBuffer(QRhiBuffer.Type.Immutable,
+ QRhiBuffer.UsageFlag.VertexBuffer, vertex_size)
self.m_vbuf.create()
- self.m_ubuf = self.m_rhi.newBuffer(QRhiBuffer.Dynamic,
- QRhiBuffer.UniformBuffer, 64)
+ self.m_ubuf = self.m_rhi.newBuffer(QRhiBuffer.Type.Dynamic,
+ QRhiBuffer.UsageFlag.UniformBuffer, 64)
self.m_ubuf.create()
self.m_srb = self.m_rhi.newShaderResourceBindings()
bindings = [
- QRhiShaderResourceBinding.uniformBuffer(0, QRhiShaderResourceBinding.VertexStage,
+ QRhiShaderResourceBinding.uniformBuffer(0, QRhiShaderResourceBinding.StageFlag.VertexStage, # noqa: E501
self.m_ubuf)
]
self.m_srb.setBindings(bindings)
self.m_pipeline = self.m_rhi.newGraphicsPipeline()
stages = [
- QRhiShaderStage(QRhiShaderStage.Vertex,
+ QRhiShaderStage(QRhiShaderStage.Type.Vertex,
getShader(":/shader_assets/color.vert.qsb")),
- QRhiShaderStage(QRhiShaderStage.Fragment,
+ QRhiShaderStage(QRhiShaderStage.Type.Fragment,
getShader(":/shader_assets/color.frag.qsb"))
]
self.m_pipeline.setShaderStages(stages)
input_bindings = [QRhiVertexInputBinding(5 * 4)] # sizeof(float)
inputLayout.setBindings(input_bindings)
attributes = [ # 4: sizeof(float)
- QRhiVertexInputAttribute(0, 0, QRhiVertexInputAttribute.Float2, 0),
- QRhiVertexInputAttribute(0, 1, QRhiVertexInputAttribute.Float3, 2 * 4)
+ QRhiVertexInputAttribute(0, 0, QRhiVertexInputAttribute.Format.Float2, 0),
+ QRhiVertexInputAttribute(0, 1, QRhiVertexInputAttribute.Format.Float3, 2 * 4)
]
inputLayout.setAttributes(attributes)
self.m_pipeline.setVertexInputLayout(inputLayout)
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the Qt Simple RHI Widget Example example from Qt v6.x"""
--- /dev/null
+Order Form Example
+==================
+
+The Order Form example shows how to generate rich text documents by combining a
+simple template with data input by the user in a dialog
+
+.. image:: orderform.webp
+ :width: 400
+ :alt: orderform screenshot
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/richtext/orderform example from Qt v5.x"""
self.letters.setCurrentIndex(tab_index)
cursor = editor.textCursor()
- cursor.movePosition(QTextCursor.Start)
+ cursor.movePosition(QTextCursor.MoveOperation.Start)
top_frame = cursor.currentFrame()
top_frame_format = top_frame.frameFormat()
top_frame_format.setPadding(16)
text_format = QTextCharFormat()
bold_format = QTextCharFormat()
- bold_format.setFontWeight(QFont.Bold)
+ bold_format.setFontWeight(QFont.Weight.Bold)
reference_frame_format = QTextFrameFormat()
reference_frame_format.setBorder(1)
reference_frame_format.setPadding(8)
- reference_frame_format.setPosition(QTextFrameFormat.FloatRight)
- reference_frame_format.setWidth(QTextLength(QTextLength.PercentageLength, 40))
+ reference_frame_format.setPosition(QTextFrameFormat.Position.FloatRight)
+ reference_frame_format.setWidth(QTextLength(QTextLength.Type.PercentageLength, 40))
cursor.insertFrame(reference_frame_format)
cursor.insertText("A company", bold_format)
cursor.insertBlock()
body_frame_format = QTextFrameFormat()
- body_frame_format.setWidth(QTextLength(QTextLength.PercentageLength, 100))
+ body_frame_format.setWidth(QTextLength(QTextLength.Type.PercentageLength, 100))
cursor.insertFrame(body_frame_format)
cursor.insertText("I would like to place an order for the following items:", text_format)
cursor.insertBlock()
order_table_format = QTextTableFormat()
- order_table_format.setAlignment(Qt.AlignHCenter)
+ order_table_format.setAlignment(Qt.AlignmentFlag.AlignHCenter)
order_table = cursor.insertTable(1, 2, order_table_format)
order_frame_format = cursor.currentFrame().frameFormat()
name_label = QLabel("Name:")
address_label = QLabel("Address:")
- address_label.setAlignment(Qt.AlignLeft | Qt.AlignTop)
+ address_label.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
self._name_edit = QLineEdit()
self._address_edit = QTextEdit()
self.setup_items_table()
- button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+ button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok
+ | QDialogButtonBox.StandardButton.Cancel)
button_box.accepted.connect(self.verify)
button_box.rejected.connect(self.reject)
for row, item in enumerate(self.items):
name = QTableWidgetItem(item)
- name.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
+ name.setFlags(Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable)
self._items_table.setItem(row, 0, name)
quantity = QTableWidgetItem('1')
self._items_table.setItem(row, 1, quantity)
for row in range(len(self.items)):
text = self._items_table.item(row, 0).text()
- quantity = int(self._items_table.item(row, 1).data(Qt.DisplayRole))
+ quantity = int(self._items_table.item(row, 1).data(Qt.ItemDataRole.DisplayRole))
order_list.append((text, max(0, quantity)))
return order_list
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/richtext/syntaxhighlighter example from Qt v5.x"""
if file_name:
in_file = QFile(file_name)
- if in_file.open(QFile.ReadOnly | QFile.Text):
+ if in_file.open(QFile.OpenModeFlag.ReadOnly | QFile.OpenModeFlag.Text):
stream = QTextStream(in_file)
self._editor.setPlainText(stream.readAll())
def setup_editor(self):
class_format = QTextCharFormat()
- class_format.setFontWeight(QFont.Bold)
- class_format.setForeground(Qt.blue)
+ class_format.setFontWeight(QFont.Weight.Bold)
+ class_format.setForeground(Qt.GlobalColor.blue)
pattern = r'^\s*class\s+\w+\(.*$'
self._highlighter.add_mapping(pattern, class_format)
function_format = QTextCharFormat()
function_format.setFontItalic(True)
- function_format.setForeground(Qt.blue)
+ function_format.setForeground(Qt.GlobalColor.blue)
pattern = r'^\s*def\s+\w+\s*\(.*\)\s*:\s*$'
self._highlighter.add_mapping(pattern, function_format)
comment_format.setBackground(QColor("#77ff77"))
self._highlighter.add_mapping(r'^\s*#.*$', comment_format)
- font = QFontDatabase.systemFont(QFontDatabase.FixedFont)
+ font = QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont)
self._editor = QPlainTextEdit()
self._editor.setFont(font)
self._highlighter.setDocument(self._editor.document())
file_menu = self.menuBar().addMenu(self.tr("&File"))
new_file_act = file_menu.addAction(self.tr("&New..."))
- new_file_act.setShortcut(QKeySequence(QKeySequence.New))
+ new_file_act.setShortcut(QKeySequence(QKeySequence.StandardKey.New))
new_file_act.triggered.connect(self.new_file)
open_file_act = file_menu.addAction(self.tr("&Open..."))
- open_file_act.setShortcut(QKeySequence(QKeySequence.Open))
+ open_file_act.setShortcut(QKeySequence(QKeySequence.StandardKey.Open))
open_file_act.triggered.connect(self.open_file)
quit_act = file_menu.addAction(self.tr("E&xit"))
- quit_act.setShortcut(QKeySequence(QKeySequence.Quit))
+ quit_act.setShortcut(QKeySequence(QKeySequence.StandardKey.Quit))
quit_act.triggered.connect(self.close)
help_menu = self.menuBar().addMenu("&Help")
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/richtext/textedit example from Qt v6.x"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtCore import (QCoreApplication, QDir, QFile, QFileInfo,
self._text_edit.cursorPositionChanged.connect(self.cursor_position_changed)
self.setCentralWidget(self._text_edit)
- self.setToolButtonStyle(Qt.ToolButtonFollowStyle)
+ self.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonFollowStyle)
self.setup_file_actions()
self.setup_edit_actions()
self.setup_text_actions()
help_menu.addAction("About &Qt", qApp.aboutQt) # noqa: F821
text_font = QFont("Helvetica")
- text_font.setStyleHint(QFont.SansSerif)
+ text_font.setStyleHint(QFont.StyleHint.SansSerif)
self._text_edit.setFont(text_font)
self.font_changed(self._text_edit.font())
self.color_changed(self._text_edit.textColor())
# Use dark text on light background on macOS, also in dark mode.
if sys.platform == 'darwin':
pal = self._text_edit.palette()
- pal.setColor(QPalette.Base, QColor(Qt.white))
- pal.setColor(QPalette.Text, QColor(Qt.black))
+ pal.setColor(QPalette.Base, QColor(Qt.GlobalColor.white))
+ pal.setColor(QPalette.Text, QColor(Qt.GlobalColor.black))
self._text_edit.setPalette(pal)
def closeEvent(self, e):
QIcon(RSRC_PATH + "/filenew.png"))
a = menu.addAction(icon, "&New", self.file_new)
tb.addAction(a)
- a.setPriority(QAction.LowPriority)
- a.setShortcut(QKeySequence.New)
+ a.setPriority(QAction.Priority.LowPriority)
+ a.setShortcut(QKeySequence.StandardKey.New)
icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentOpen,
QIcon(RSRC_PATH + "/fileopen.png"))
a = menu.addAction(icon, "&Open...", self.file_open)
- a.setShortcut(QKeySequence.Open)
+ a.setShortcut(QKeySequence.StandardKey.Open)
tb.addAction(a)
menu.addSeparator()
icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentSave,
QIcon(RSRC_PATH + "/filesave.png"))
self._action_save = menu.addAction(icon, "&Save", self.file_save)
- self._action_save.setShortcut(QKeySequence.Save)
+ self._action_save.setShortcut(QKeySequence.StandardKey.Save)
self._action_save.setEnabled(False)
tb.addAction(self._action_save)
a = menu.addAction("Save &As...", self.file_save_as)
- a.setPriority(QAction.LowPriority)
+ a.setPriority(QAction.Priority.LowPriority)
menu.addSeparator()
icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentPrint,
QIcon(RSRC_PATH + "/fileprint.png"))
a = menu.addAction(icon, "&Print...", self.file_print)
- a.setPriority(QAction.LowPriority)
- a.setShortcut(QKeySequence.Print)
+ a.setPriority(QAction.Priority.LowPriority)
+ a.setShortcut(QKeySequence.StandardKey.Print)
tb.addAction(a)
icon = QIcon.fromTheme("fileprint", QIcon(RSRC_PATH + "/fileprint.png"))
icon = QIcon.fromTheme("exportpdf", QIcon(RSRC_PATH + "/exportpdf.png"))
a = menu.addAction(icon, "&Export PDF...", self.file_print_pdf)
- a.setPriority(QAction.LowPriority)
- a.setShortcut(Qt.CTRL | Qt.Key_D)
+ a.setPriority(QAction.Priority.LowPriority)
+ a.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_D)
tb.addAction(a)
menu.addSeparator()
a = menu.addAction("&Quit", self.close)
- a.setShortcut(Qt.CTRL | Qt.Key_Q)
+ a.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_Q)
def setup_edit_actions(self):
tb = self.addToolBar("Edit self.actions")
icon = QIcon.fromTheme(QIcon.ThemeIcon.EditUndo,
QIcon(RSRC_PATH + "/editundo.png"))
self._action_undo = menu.addAction(icon, "&Undo", self._text_edit.undo)
- self._action_undo.setShortcut(QKeySequence.Undo)
+ self._action_undo.setShortcut(QKeySequence.StandardKey.Undo)
tb.addAction(self._action_undo)
icon = QIcon.fromTheme(QIcon.ThemeIcon.EditRedo,
QIcon(RSRC_PATH + "/editredo.png"))
self._action_redo = menu.addAction(icon, "&Redo", self._text_edit.redo)
- self._action_redo.setPriority(QAction.LowPriority)
- self._action_redo.setShortcut(QKeySequence.Redo)
+ self._action_redo.setPriority(QAction.Priority.LowPriority)
+ self._action_redo.setShortcut(QKeySequence.StandardKey.Redo)
tb.addAction(self._action_redo)
menu.addSeparator()
icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCut,
QIcon(RSRC_PATH + "/editcut.png"))
self._action_cut = menu.addAction(icon, "Cu&t", self._text_edit.cut)
- self._action_cut.setPriority(QAction.LowPriority)
- self._action_cut.setShortcut(QKeySequence.Cut)
+ self._action_cut.setPriority(QAction.Priority.LowPriority)
+ self._action_cut.setShortcut(QKeySequence.StandardKey.Cut)
tb.addAction(self._action_cut)
icon = QIcon.fromTheme(QIcon.ThemeIcon.EditCopy,
QIcon(RSRC_PATH + "/editcopy.png"))
self._action_copy = menu.addAction(icon, "&Copy", self._text_edit.copy)
- self._action_copy.setPriority(QAction.LowPriority)
- self._action_copy.setShortcut(QKeySequence.Copy)
+ self._action_copy.setPriority(QAction.Priority.LowPriority)
+ self._action_copy.setShortcut(QKeySequence.StandardKey.Copy)
tb.addAction(self._action_copy)
icon = QIcon.fromTheme(QIcon.ThemeIcon.EditPaste,
QIcon(RSRC_PATH + "/editpaste.png"))
self._action_paste = menu.addAction(icon, "&Paste", self._text_edit.paste)
- self._action_paste.setPriority(QAction.LowPriority)
- self._action_paste.setShortcut(QKeySequence.Paste)
+ self._action_paste.setPriority(QAction.Priority.LowPriority)
+ self._action_paste.setShortcut(QKeySequence.StandardKey.Paste)
tb.addAction(self._action_paste)
md = QGuiApplication.clipboard().mimeData()
icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatTextBold,
QIcon(RSRC_PATH + "/textbold.png"))
self._action_text_bold = menu.addAction(icon, "&Bold", self.text_bold)
- self._action_text_bold.setShortcut(Qt.CTRL | Qt.Key_B)
- self._action_text_bold.setPriority(QAction.LowPriority)
+ self._action_text_bold.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_B)
+ self._action_text_bold.setPriority(QAction.Priority.LowPriority)
bold = QFont()
bold.setBold(True)
self._action_text_bold.setFont(bold)
icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatTextItalic,
QIcon(RSRC_PATH + "/textitalic.png"))
self._action_text_italic = menu.addAction(icon, "&Italic", self.text_italic)
- self._action_text_italic.setPriority(QAction.LowPriority)
- self._action_text_italic.setShortcut(Qt.CTRL | Qt.Key_I)
+ self._action_text_italic.setPriority(QAction.Priority.LowPriority)
+ self._action_text_italic.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_I)
italic = QFont()
italic.setItalic(True)
self._action_text_italic.setFont(italic)
QIcon(RSRC_PATH + "/textunder.png"))
self._action_text_underline = menu.addAction(icon, "&Underline",
self.text_underline)
- self._action_text_underline.setShortcut(Qt.CTRL | Qt.Key_U)
- self._action_text_underline.setPriority(QAction.LowPriority)
+ self._action_text_underline.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_U)
+ self._action_text_underline.setPriority(QAction.Priority.LowPriority)
underline = QFont()
underline.setUnderline(True)
self._action_text_underline.setFont(underline)
icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatJustifyLeft,
QIcon(RSRC_PATH + "/textleft.png"))
self._action_align_left = QAction(icon, "&Left", self)
- self._action_align_left.setShortcut(Qt.CTRL | Qt.Key_L)
+ self._action_align_left.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_L)
self._action_align_left.setCheckable(True)
- self._action_align_left.setPriority(QAction.LowPriority)
+ self._action_align_left.setPriority(QAction.Priority.LowPriority)
icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatJustifyCenter,
QIcon(RSRC_PATH + "/textcenter.png"))
self._action_align_center = QAction(icon, "C&enter", self)
- self._action_align_center.setShortcut(Qt.CTRL | Qt.Key_E)
+ self._action_align_center.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_E)
self._action_align_center.setCheckable(True)
- self._action_align_center.setPriority(QAction.LowPriority)
+ self._action_align_center.setPriority(QAction.Priority.LowPriority)
icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatJustifyRight,
QIcon(RSRC_PATH + "/textright.png"))
self._action_align_right = QAction(icon, "&Right", self)
- self._action_align_right.setShortcut(Qt.CTRL | Qt.Key_R)
+ self._action_align_right.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_R)
self._action_align_right.setCheckable(True)
- self._action_align_right.setPriority(QAction.LowPriority)
+ self._action_align_right.setPriority(QAction.Priority.LowPriority)
icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatJustifyFill,
QIcon(RSRC_PATH + "/textjustify.png"))
self._action_align_justify = QAction(icon, "&Justify", self)
- self._action_align_justify.setShortcut(Qt.CTRL | Qt.Key_J)
+ self._action_align_justify.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_J)
self._action_align_justify.setCheckable(True)
- self._action_align_justify.setPriority(QAction.LowPriority)
+ self._action_align_justify.setPriority(QAction.Priority.LowPriority)
icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatIndentMore,
QIcon(RSRC_PATH + "/format-indent-more.png"))
self._action_indent_more = menu.addAction(icon, "&Indent", self.indent)
- self._action_indent_more.setShortcut(Qt.CTRL | Qt.Key_BracketRight)
- self._action_indent_more.setPriority(QAction.LowPriority)
+ self._action_indent_more.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_BracketRight)
+ self._action_indent_more.setPriority(QAction.Priority.LowPriority)
icon = QIcon.fromTheme(QIcon.ThemeIcon.FormatIndentLess,
QIcon(RSRC_PATH + "/format-indent-less.png"))
self._action_indent_less = menu.addAction(icon, "&Unindent",
self.unindent)
- self._action_indent_less.setShortcut(Qt.CTRL | Qt.Key_BracketLeft)
- self._action_indent_less.setPriority(QAction.LowPriority)
+ self._action_indent_less.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_BracketLeft)
+ self._action_indent_less.setPriority(QAction.Priority.LowPriority)
# Make sure the alignLeft is always left of the alignRight
align_group = QActionGroup(self)
menu.addSeparator()
pix = QPixmap(16, 16)
- pix.fill(Qt.black)
+ pix.fill(Qt.GlobalColor.black)
self._action_text_color = menu.addAction(pix, "&Color...", self.text_color)
tb.addAction(self._action_text_color)
QIcon(RSRC_PATH + "/checkbox-checked.png"))
self._action_toggle_check_state = menu.addAction(icon, "Chec&ked")
self._action_toggle_check_state.toggled.connect(self.set_checked)
- self._action_toggle_check_state.setShortcut(Qt.CTRL | Qt.Key_K)
+ self._action_toggle_check_state.setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_K)
self._action_toggle_check_state.setCheckable(True)
- self._action_toggle_check_state.setPriority(QAction.LowPriority)
+ self._action_toggle_check_state.setPriority(QAction.Priority.LowPriority)
tb.addAction(self._action_toggle_check_state)
tb = self.addToolBar("Format self.actions")
- tb.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea)
- self.addToolBarBreak(Qt.TopToolBarArea)
+ tb.setAllowedAreas(Qt.ToolBarArea.TopToolBarArea | Qt.ToolBarArea.BottomToolBarArea)
+ self.addToolBarBreak(Qt.ToolBarArea.TopToolBarArea)
self.addToolBar(tb)
self._combo_style = QComboBox(tb)
if not QFile.exists(f):
return False
file = QFile(f)
- if not file.open(QFile.ReadOnly):
+ if not file.open(QFile.OpenModeFlag.ReadOnly):
return False
data = file.readAll()
text = data.data().decode('utf8')
if mime_type_name == "text/html":
file_url = QUrl(f) if f[0] == ':' else QUrl.fromLocalFile(f)
- options = QUrl.FormattingOptions(QUrl.RemoveFilename)
+ options = QUrl.FormattingOptions(QUrl.UrlFormattingOption.RemoveFilename)
self._text_edit.document().setBaseUrl(file_url.adjusted(options))
self._text_edit.setHtml(text)
elif mime_type_name == "text/markdown":
@Slot()
def underline_color(self):
- col = QColorDialog.getColor(Qt.black, self)
+ col = QColorDialog.getColor(Qt.GlobalColor.black, self)
if not col.isValid():
return
fmt = QTextCharFormat()
@Slot(QAction)
def text_align(self, a):
if a == self._action_align_left:
- self._text_edit.setAlignment(Qt.AlignLeft | Qt.AlignAbsolute)
+ self._text_edit.setAlignment(Qt.AlignmentFlag.AlignLeft
+ | Qt.AlignmentFlag.AlignAbsolute)
elif a == self._action_align_center:
- self._text_edit.setAlignment(Qt.AlignHCenter)
+ self._text_edit.setAlignment(Qt.AlignmentFlag.AlignHCenter)
elif a == self._action_align_right:
- self._text_edit.setAlignment(Qt.AlignRight | Qt.AlignAbsolute)
+ self._text_edit.setAlignment(Qt.AlignmentFlag.AlignRight
+ | Qt.AlignmentFlag.AlignAbsolute)
elif a == self._action_align_justify:
- self._text_edit.setAlignment(Qt.AlignJustify)
+ self._text_edit.setAlignment(Qt.AlignmentFlag.AlignJustify)
@Slot(bool)
def set_checked(self, checked):
self._action_text_color.setIcon(pix)
def alignment_changed(self, a):
- if a & Qt.AlignLeft:
+ if a & Qt.AlignmentFlag.AlignLeft:
self._action_align_left.setChecked(True)
- elif a & Qt.AlignHCenter:
+ elif a & Qt.AlignmentFlag.AlignHCenter:
self._action_align_center.setChecked(True)
- elif a & Qt.AlignRight:
+ elif a & Qt.AlignmentFlag.AlignRight:
self._action_align_right.setChecked(True)
- elif a & Qt.AlignJustify:
+ elif a & Qt.AlignmentFlag.AlignJustify:
self._action_align_justify.setChecked(True)
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/richtext/textobject example from Qt v5.x"""
from PySide6.QtSvg import QSvgRenderer
-SVG_TEXT_FORMAT = QTextFormat.UserObject + 1
+SVG_TEXT_FORMAT = QTextFormat.ObjectTypes.UserObject + 1
SVG_DATA = 1
file_name = self._file_name_line_edit.text()
file = QFile(file_name)
- if not file.open(QIODevice.ReadOnly):
+ if not file.open(QIODevice.OpenModeFlag.ReadOnly):
reason = file.errorString()
message = f"Could not open '{file_name}': {reason}"
QMessageBox.warning(self, "Error Opening File", message)
--- /dev/null
+Thread Signals Examples
+=======================
+
+This example demonstrates a PySide6 application that uses threads and signals
+to perform background tasks.
+
+.. image:: thread_signals.png
+ :width: 400
+ :alt: thread_signals screenshot
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtCore import QObject, QThread, Signal, Slot
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""
PySide6 port of Qt6 example qtbase/examples/widgets/tools/regularexpression
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import re
import logging
def createHorizontalSeparator() -> QFrame:
result = QFrame()
- result.setFrameStyle(QFrame.HLine | QFrame.Sunken)
+ result.setFrameStyle(QFrame.Shape.HLine | QFrame.Shadow.Sunken)
return result
def createVerticalSeparator() -> QFrame:
result = QFrame()
- result.setFrameStyle(QFrame.VLine | QFrame.Sunken)
+ result.setFrameStyle(QFrame.Shape.VLine | QFrame.Shadow.Sunken)
return result
def contextMenuEvent(self, event: QContextMenuEvent) -> None:
menu = self.createStandardContextMenu()
- menu.setAttribute(Qt.WA_DeleteOnClose)
+ menu.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
menu.addSeparator()
self.escapeSelectionAction.setEnabled(self.hasSelectedText())
menu.addAction(self.escapeSelectionAction)
self.setReadOnly(True)
self.disablePalette: QPalette = self.palette()
self.disablePalette.setBrush(
- QPalette.Base,
- self.disablePalette.brush(QPalette.Disabled, QPalette.Base),
+ QPalette.ColorRole.Base,
+ self.disablePalette.brush(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Base),
)
self.setPalette(self.disablePalette)
self.copyAction.triggered.connect(
lambda: QGuiApplication.clipboard().setText(self.text())
)
- self.addAction(self.copyAction, QLineEdit.TrailingPosition)
+ self.addAction(self.copyAction, QLineEdit.ActionPosition.TrailingPosition)
class RegularExpressionDialog(QDialog):
def setTextColor(self, widget: QWidget, color: QColor):
self.palette: QPalette = widget.palette()
- self.palette.setColor(QPalette.Text, color)
+ self.palette.setColor(QPalette.ColorRole.Text, color)
widget.setPalette(self.palette)
@Slot()
self.setTextColor(
self.patternLineEdit,
- self.subjectTextEdit.palette().color(QPalette.Text),
+ self.subjectTextEdit.palette().color(QPalette.ColorRole.Text),
)
self.matchDetailsTreeWidget.clear()
self.namedGroupsTreeWidget.clear()
matchType: QRegularExpression.MatchType = QRegularExpression.MatchType(
self.matchTypeComboBox.currentData()
)
- patternOptions = QRegularExpression.NoPatternOption
- matchOptions = QRegularExpression.NoMatchOption
+ patternOptions = QRegularExpression.PatternOption.NoPatternOption
+ matchOptions = QRegularExpression.MatchOption.NoMatchOption
if self.anchoredMatchOptionCheckBox.isChecked():
- matchOptions |= QRegularExpression.AnchorAtOffsetMatchOption
+ matchOptions |= QRegularExpression.MatchOption.AnchorAtOffsetMatchOption
if self.dontCheckSubjectStringMatchOptionCheckBox.isChecked():
- matchOptions |= QRegularExpression.DontCheckSubjectStringMatchOption
+ matchOptions |= QRegularExpression.MatchOption.DontCheckSubjectStringMatchOption
if self.caseInsensitiveOptionCheckBox.isChecked():
- patternOptions |= QRegularExpression.CaseInsensitiveOption
+ patternOptions |= QRegularExpression.PatternOption.CaseInsensitiveOption
if self.dotMatchesEverythingOptionCheckBox.isChecked():
- patternOptions |= QRegularExpression.DotMatchesEverythingOption
+ patternOptions |= QRegularExpression.PatternOption.DotMatchesEverythingOption
if self.multilineOptionCheckBox.isChecked():
- patternOptions |= QRegularExpression.MultilineOption
+ patternOptions |= QRegularExpression.PatternOption.MultilineOption
if self.extendedPatternSyntaxOptionCheckBox.isChecked():
- patternOptions |= QRegularExpression.ExtendedPatternSyntaxOption
+ patternOptions |= QRegularExpression.PatternOption.ExtendedPatternSyntaxOption
if self.invertedGreedinessOptionCheckBox.isChecked():
- patternOptions |= QRegularExpression.InvertedGreedinessOption
+ patternOptions |= QRegularExpression.PatternOption.InvertedGreedinessOption
if self.dontCaptureOptionCheckBox.isChecked():
- patternOptions |= QRegularExpression.DontCaptureOption
+ patternOptions |= QRegularExpression.PatternOption.DontCaptureOption
if self.useUnicodePropertiesOptionCheckBox.isChecked():
- patternOptions |= QRegularExpression.UseUnicodePropertiesOption
+ patternOptions |= QRegularExpression.PatternOption.UseUnicodePropertiesOption
self.regularExpression.setPatternOptions(patternOptions)
self.horizontalLayout.addWidget(createVerticalSeparator())
self.horizontalLayout.addWidget(self.setupInfoUi())
- self._font = QFontDatabase.systemFont(QFontDatabase.FixedFont)
+ self._font = QFontDatabase.systemFont(QFontDatabase.SystemFont.FixedFont)
self.patternLineEdit.setFont(self._font)
self.rawStringLiteralLineEdit.setFont(self._font)
self.escapedPatternLineEdit.setFont(self._font)
container = QWidget()
form_layout = QFormLayout(container)
- form_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
+ form_layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
form_layout.setContentsMargins(QMargins())
form_layout.addRow(QLabel("<h3>Options</h3>"))
form_layout.addRow("Match &offset:", self.offsetSpinBox)
self.matchTypeComboBox = QComboBox()
- self.matchTypeComboBox.addItem("Normal", QRegularExpression.NormalMatch)
+ self.matchTypeComboBox.addItem("Normal", QRegularExpression.MatchType.NormalMatch)
self.matchTypeComboBox.addItem(
"Partial prefer complete",
- QRegularExpression.PartialPreferCompleteMatch,
+ QRegularExpression.MatchType.PartialPreferCompleteMatch,
)
self.matchTypeComboBox.addItem(
- "Partial prefer first", QRegularExpression.PartialPreferFirstMatch
+ "Partial prefer first", QRegularExpression.MatchType.PartialPreferFirstMatch
)
- self.matchTypeComboBox.addItem("No match", QRegularExpression.NoMatch)
+ self.matchTypeComboBox.addItem("No match", QRegularExpression.MatchType.NoMatch)
form_layout.addRow("Match &type:", self.matchTypeComboBox)
self.dontCheckSubjectStringMatchOptionCheckBox = QCheckBox(
container = QWidget()
form_layout = QFormLayout(container)
- form_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
+ form_layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
form_layout.setContentsMargins(QMargins())
self.matchInfoLabel = QLabel("<h3>Match information</h3>")
self.matchDetailsTreeWidget.setHeaderLabels(
["Match index", "Group index", "Captured string"]
)
- self.matchDetailsTreeWidget.setSizeAdjustPolicy(QTreeWidget.AdjustToContents)
+ self.matchDetailsTreeWidget.setSizeAdjustPolicy(QTreeWidget.SizeAdjustPolicy.AdjustToContents) # noqa: E501
form_layout.addRow("Match details:", self.matchDetailsTreeWidget)
form_layout.addRow(createHorizontalSeparator())
self.namedGroupsTreeWidget = QTreeWidget()
self.namedGroupsTreeWidget.setHeaderLabels(["Index", "Named group"])
- self.namedGroupsTreeWidget.setSizeAdjustPolicy(QTreeWidget.AdjustToContents)
+ self.namedGroupsTreeWidget.setSizeAdjustPolicy(QTreeWidget.SizeAdjustPolicy.AdjustToContents) # noqa: E501
self.namedGroupsTreeWidget.setRootIsDecorated(False)
form_layout.addRow("Named groups:", self.namedGroupsTreeWidget)
def setupTextUi(self):
container = QWidget()
form_layout = QFormLayout(container)
- form_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
+ form_layout.setFieldGrowthPolicy(QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow)
form_layout.setContentsMargins(QMargins())
self.regexpAndSubjectLabel = QLabel(
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
main_layout = QGridLayout()
main_layout.addWidget(name_label, 0, 0)
main_layout.addWidget(self._name_line, 0, 1)
- main_layout.addWidget(address_label, 1, 0, Qt.AlignTop)
+ main_layout.addWidget(address_label, 1, 0, Qt.AlignmentFlag.AlignTop)
main_layout.addWidget(self._address_text, 1, 1)
self.setLayout(main_layout)
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
class SortedDict(dict):
- class Iterator(object):
+ class Iterator:
def __init__(self, sorted_dict):
self._dict = sorted_dict
self._keys = sorted(self._dict.keys())
self._cancel_button.clicked.connect(self.cancel)
button_layout_1 = QVBoxLayout()
- button_layout_1.addWidget(self._add_button, Qt.AlignTop)
+ button_layout_1.addWidget(self._add_button, Qt.AlignmentFlag.AlignTop)
button_layout_1.addWidget(self._submit_button)
button_layout_1.addWidget(self._cancel_button)
button_layout_1.addStretch()
main_layout = QGridLayout()
main_layout.addWidget(name_label, 0, 0)
main_layout.addWidget(self._name_line, 0, 1)
- main_layout.addWidget(address_label, 1, 0, Qt.AlignTop)
+ main_layout.addWidget(address_label, 1, 0, Qt.AlignmentFlag.AlignTop)
main_layout.addWidget(self._address_text, 1, 1)
main_layout.addLayout(button_layout_1, 1, 2)
self._address_text.clear()
self._name_line.setReadOnly(False)
- self._name_line.setFocus(Qt.OtherFocusReason)
+ self._name_line.setFocus(Qt.FocusReason.OtherFocusReason)
self._address_text.setReadOnly(False)
self._add_button.setEnabled(False)
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
class SortedDict(dict):
- class Iterator(object):
+ class Iterator:
def __init__(self, sorted_dict):
self._dict = sorted_dict
self._keys = sorted(self._dict.keys())
self._previous_button.clicked.connect(self.previous)
button_layout_1 = QVBoxLayout()
- button_layout_1.addWidget(self._add_button, Qt.AlignTop)
+ button_layout_1.addWidget(self._add_button, Qt.AlignmentFlag.AlignTop)
button_layout_1.addWidget(self._submit_button)
button_layout_1.addWidget(self._cancel_button)
button_layout_1.addStretch()
main_layout = QGridLayout()
main_layout.addWidget(name_label, 0, 0)
main_layout.addWidget(self._name_line, 0, 1)
- main_layout.addWidget(address_label, 1, 0, Qt.AlignTop)
+ main_layout.addWidget(address_label, 1, 0, Qt.AlignmentFlag.AlignTop)
main_layout.addWidget(self._address_text, 1, 1)
main_layout.addLayout(button_layout_1, 1, 2)
main_layout.addLayout(button_layout_2, 3, 1)
self._address_text.clear()
self._name_line.setReadOnly(False)
- self._name_line.setFocus(Qt.OtherFocusReason)
+ self._name_line.setFocus(Qt.FocusReason.OtherFocusReason)
self._address_text.setReadOnly(False)
self._add_button.setEnabled(False)
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
class SortedDict(dict):
- class Iterator(object):
+ class Iterator:
def __init__(self, sorted_dict):
self._dict = sorted_dict
self._keys = sorted(self._dict.keys())
main_layout = QGridLayout()
main_layout.addWidget(name_label, 0, 0)
main_layout.addWidget(self._name_line, 0, 1)
- main_layout.addWidget(address_label, 1, 0, Qt.AlignTop)
+ main_layout.addWidget(address_label, 1, 0, Qt.AlignmentFlag.AlignTop)
main_layout.addWidget(self._address_text, 1, 1)
main_layout.addLayout(button_layout_1, 1, 2)
main_layout.addLayout(button_layout_2, 3, 1)
if self._current_mode in (self.AddingMode, self.EditingMode):
self._name_line.setReadOnly(False)
- self._name_line.setFocus(Qt.OtherFocusReason)
+ self._name_line.setFocus(Qt.FocusReason.OtherFocusReason)
self._address_text.setReadOnly(False)
self._add_button.setEnabled(False)
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
class SortedDict(dict):
- class Iterator(object):
+ class Iterator:
def __init__(self, sorted_dict):
self._dict = sorted_dict
self._keys = sorted(self._dict.keys())
main_layout = QGridLayout()
main_layout.addWidget(name_label, 0, 0)
main_layout.addWidget(self._name_line, 0, 1)
- main_layout.addWidget(address_label, 1, 0, Qt.AlignTop)
+ main_layout.addWidget(address_label, 1, 0, Qt.AlignmentFlag.AlignTop)
main_layout.addWidget(self._address_text, 1, 1)
main_layout.addLayout(button_layout_1, 1, 2)
main_layout.addLayout(button_layout_2, 2, 1)
if self._current_mode in (self.AddingMode, self.EditingMode):
self._name_line.setReadOnly(False)
- self._name_line.setFocus(Qt.OtherFocusReason)
+ self._name_line.setFocus(Qt.FocusReason.OtherFocusReason)
self._address_text.setReadOnly(False)
self._add_button.setEnabled(False)
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import pickle
import sys
class SortedDict(dict):
- class Iterator(object):
+ class Iterator:
def __init__(self, sorted_dict):
self._dict = sorted_dict
self._keys = sorted(self._dict.keys())
main_layout = QGridLayout()
main_layout.addWidget(name_label, 0, 0)
main_layout.addWidget(self._name_line, 0, 1)
- main_layout.addWidget(address_label, 1, 0, Qt.AlignTop)
+ main_layout.addWidget(address_label, 1, 0, Qt.AlignmentFlag.AlignTop)
main_layout.addWidget(self._address_text, 1, 1)
main_layout.addLayout(button_layout_1, 1, 2)
main_layout.addLayout(button_layout_2, 2, 1)
if self._current_mode in (self.AddingMode, self.EditingMode):
self._name_line.setReadOnly(False)
- self._name_line.setFocus(Qt.OtherFocusReason)
+ self._name_line.setFocus(Qt.FocusReason.OtherFocusReason)
self._address_text.setReadOnly(False)
self._add_button.setEnabled(False)
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import pickle
import sys
class SortedDict(dict):
- class Iterator(object):
+ class Iterator:
def __init__(self, sorted_dict):
self._dict = sorted_dict
self._keys = sorted(self._dict.keys())
main_layout = QGridLayout()
main_layout.addWidget(name_label, 0, 0)
main_layout.addWidget(self._name_line, 0, 1)
- main_layout.addWidget(address_label, 1, 0, Qt.AlignTop)
+ main_layout.addWidget(address_label, 1, 0, Qt.AlignmentFlag.AlignTop)
main_layout.addWidget(self._address_text, 1, 1)
main_layout.addLayout(button_layout_1, 1, 2)
main_layout.addLayout(button_layout_2, 2, 1)
if self._current_mode in (self.AddingMode, self.EditingMode):
self._name_line.setReadOnly(False)
- self._name_line.setFocus(Qt.OtherFocusReason)
+ self._name_line.setFocus(Qt.FocusReason.OtherFocusReason)
self._address_text.setReadOnly(False)
self._add_button.setEnabled(False)
out_file = QFile(file_name)
- if not out_file.open(QIODevice.WriteOnly):
+ if not out_file.open(QIODevice.OpenModeFlag.WriteOnly):
QMessageBox.information(self, "Unable to open file", out_file.errorString())
return
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
# PySide6 tutorial 1
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
# PySide6 tutorial 10
super().__init__(parent)
lcd = QLCDNumber(2)
- self.slider = QSlider(Qt.Horizontal)
+ self.slider = QSlider(Qt.Orientation.Horizontal)
self.slider.setRange(0, 99)
self.slider.setValue(0)
def paintEvent(self, event):
with QPainter(self) as painter:
- painter.setPen(Qt.NoPen)
- painter.setBrush(Qt.blue)
+ painter.setPen(Qt.PenStyle.NoPen)
+ painter.setBrush(Qt.GlobalColor.blue)
painter.translate(0, self.height())
painter.drawPie(QRect(-35, -35, 70, 70), 0, 90 * 16)
super().__init__(parent)
quit = QPushButton("&Quit")
- quit.setFont(QFont("Times", 18, QFont.Bold))
+ quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
quit.clicked.connect(qApp.quit) # noqa: F821
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
# PySide6 tutorial 11
super().__init__(parent)
lcd = QLCDNumber(2)
- self.slider = QSlider(Qt.Horizontal)
+ self.slider = QSlider(Qt.Orientation.Horizontal)
self.slider.setRange(0, 99)
self.slider.setValue(0)
self.paint_shot(painter)
def paint_shot(self, painter):
- painter.setPen(Qt.NoPen)
- painter.setBrush(Qt.black)
+ painter.setPen(Qt.PenStyle.NoPen)
+ painter.setBrush(Qt.GlobalColor.black)
painter.drawRect(self.shot_rect())
barrel_rect = QRect(33, -4, 15, 8)
def paint_cannon(self, painter):
- painter.setPen(Qt.NoPen)
- painter.setBrush(Qt.blue)
+ painter.setPen(Qt.PenStyle.NoPen)
+ painter.setBrush(Qt.GlobalColor.blue)
painter.save()
painter.translate(0, self.height())
super().__init__(parent)
quit = QPushButton("&Quit")
- quit.setFont(QFont("Times", 18, QFont.Bold))
+ quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
quit.clicked.connect(qApp.quit) # noqa: F821
cannon_field.force_changed.connect(force.set_value)
shoot = QPushButton("&Shoot")
- shoot.setFont(QFont("Times", 18, QFont.Bold))
+ shoot.setFont(QFont("Times", 18, QFont.Weight.Bold))
shoot.clicked.connect(cannon_field.shoot)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
# PySide6 tutorial 12
def init(self):
lcd = QLCDNumber(2)
- self.slider = QSlider(Qt.Horizontal)
+ self.slider = QSlider(Qt.Orientation.Horizontal)
self.slider.setRange(0, 99)
self.slider.setValue(0)
self.label = QLabel()
- self.label.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
+ self.label.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop)
self.slider.valueChanged.connect(lcd.display)
self.slider.valueChanged.connect(self.value_changed)
self.paint_target(painter)
def paint_shot(self, painter):
- painter.setPen(Qt.NoPen)
- painter.setBrush(Qt.black)
+ painter.setPen(Qt.PenStyle.NoPen)
+ painter.setBrush(Qt.GlobalColor.black)
painter.drawRect(self.shot_rect())
def paint_target(self, painter):
- painter.setPen(Qt.black)
- painter.setBrush(Qt.red)
+ painter.setPen(Qt.GlobalColor.black)
+ painter.setBrush(Qt.GlobalColor.red)
painter.drawRect(self.target_rect())
barrel_rect = QRect(33, -4, 15, 8)
def paint_cannon(self, painter):
- painter.setPen(Qt.NoPen)
- painter.setBrush(Qt.blue)
+ painter.setPen(Qt.PenStyle.NoPen)
+ painter.setBrush(Qt.GlobalColor.blue)
painter.save()
painter.translate(0, self.height())
super().__init__(parent)
quit = QPushButton("&Quit")
- quit.setFont(QFont("Times", 18, QFont.Bold))
+ quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
quit.clicked.connect(qApp.quit) # noqa: F821
cannon_field.force_changed.connect(force.set_value)
shoot = QPushButton("&Shoot")
- shoot.setFont(QFont("Times", 18, QFont.Bold))
+ shoot.setFont(QFont("Times", 18, QFont.Weight.Bold))
shoot.clicked.connect(cannon_field.shoot)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
# PySide6 tutorial 13
def init(self):
lcd = QLCDNumber(2)
- self.slider = QSlider(Qt.Horizontal)
+ self.slider = QSlider(Qt.Orientation.Horizontal)
self.slider.setRange(0, 99)
self.slider.setValue(0)
self.label = QLabel()
- self.label.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
- self.label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
+ self.label.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop)
+ self.label.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
self.slider.valueChanged.connect(lcd.display)
self.slider.valueChanged.connect(self.value_changed)
def paintEvent(self, event):
with QPainter(self) as painter:
if self._game_ended:
- painter.setPen(Qt.black)
- painter.setFont(QFont("Courier", 48, QFont.Bold))
- painter.drawText(self.rect(), Qt.AlignCenter, "Game Over")
+ painter.setPen(Qt.GlobalColor.black)
+ painter.setFont(QFont("Courier", 48, QFont.Weight.Bold))
+ painter.drawText(self.rect(), Qt.AlignmentFlag.AlignCenter, "Game Over")
self.paint_cannon(painter)
if self.is_shooting():
self.paint_target(painter)
def paint_shot(self, painter):
- painter.setPen(Qt.NoPen)
- painter.setBrush(Qt.black)
+ painter.setPen(Qt.PenStyle.NoPen)
+ painter.setBrush(Qt.GlobalColor.black)
painter.drawRect(self.shot_rect())
def paint_target(self, painter):
- painter.setPen(Qt.black)
- painter.setBrush(Qt.red)
+ painter.setPen(Qt.GlobalColor.black)
+ painter.setBrush(Qt.GlobalColor.red)
painter.drawRect(self.target_rect())
barrel_rect = QRect(33, -4, 15, 8)
def paint_cannon(self, painter):
- painter.setPen(Qt.NoPen)
- painter.setBrush(Qt.blue)
+ painter.setPen(Qt.PenStyle.NoPen)
+ painter.setBrush(Qt.GlobalColor.blue)
painter.save()
painter.translate(0, self.height())
super().__init__(parent)
quit = QPushButton("&Quit")
- quit.setFont(QFont("Times", 18, QFont.Bold))
+ quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
quit.clicked.connect(qApp.quit) # noqa: F821
self._cannon_field.missed.connect(self.missed)
shoot = QPushButton("&Shoot")
- shoot.setFont(QFont("Times", 18, QFont.Bold))
+ shoot.setFont(QFont("Times", 18, QFont.Weight.Bold))
shoot.clicked.connect(self.fire)
self._cannon_field.can_shoot.connect(shoot.setEnabled)
restart = QPushButton("&New Game")
- restart.setFont(QFont("Times", 18, QFont.Bold))
+ restart.setFont(QFont("Times", 18, QFont.Weight.Bold))
restart.clicked.connect(self.new_game)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
# PySide6 tutorial 14
def init(self):
lcd = QLCDNumber(2)
- self.slider = QSlider(Qt.Horizontal)
+ self.slider = QSlider(Qt.Orientation.Horizontal)
self.slider.setRange(0, 99)
self.slider.setValue(0)
self.label = QLabel()
- self.label.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
- self.label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
+ self.label.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop)
+ self.label.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
self.slider.valueChanged.connect(lcd.display)
self.slider.valueChanged.connect(self.value_changed)
self.update(region)
def mousePressEvent(self, event):
- if event.button() != Qt.LeftButton:
+ if event.button() != Qt.MouseButton.LeftButton:
return
if self.barrel_hit(event.position().toPoint()):
self._barrel_pressed = True
self.set_angle(round(rad * 180 / math.pi))
def mouseReleaseEvent(self, event):
- if event.button() == Qt.LeftButton:
+ if event.button() == Qt.MouseButton.LeftButton:
self._barrel_pressed = False
def paintEvent(self, event):
with QPainter(self) as painter:
if self._game_ended:
- painter.setPen(Qt.black)
- painter.setFont(QFont("Courier", 48, QFont.Bold))
- painter.drawText(self.rect(), Qt.AlignCenter, "Game Over")
+ painter.setPen(Qt.GlobalColor.black)
+ painter.setFont(QFont("Courier", 48, QFont.Weight.Bold))
+ painter.drawText(self.rect(), Qt.AlignmentFlag.AlignCenter, "Game Over")
self.paint_cannon(painter)
self.paint_barrier(painter)
self.paint_target(painter)
def paint_shot(self, painter):
- painter.setPen(Qt.NoPen)
- painter.setBrush(Qt.black)
+ painter.setPen(Qt.PenStyle.NoPen)
+ painter.setBrush(Qt.GlobalColor.black)
painter.drawRect(self.shot_rect())
def paint_target(self, painter):
- painter.setPen(Qt.black)
- painter.setBrush(Qt.red)
+ painter.setPen(Qt.GlobalColor.black)
+ painter.setBrush(Qt.GlobalColor.red)
painter.drawRect(self.target_rect())
def paint_barrier(self, painter):
- painter.setPen(Qt.black)
- painter.setBrush(Qt.yellow)
+ painter.setPen(Qt.GlobalColor.black)
+ painter.setBrush(Qt.GlobalColor.yellow)
painter.drawRect(self.barrier_rect())
barrel_rect = QRect(33, -4, 15, 8)
def paint_cannon(self, painter):
- painter.setPen(Qt.NoPen)
- painter.setBrush(Qt.blue)
+ painter.setPen(Qt.PenStyle.NoPen)
+ painter.setBrush(Qt.GlobalColor.blue)
painter.save()
painter.translate(0, self.height())
super().__init__(parent)
quit = QPushButton("&Quit")
- quit.setFont(QFont("Times", 18, QFont.Bold))
+ quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
quit.clicked.connect(qApp.quit) # noqa: F821
force.set_range(10, 50)
cannon_box = QFrame()
- cannon_box.setFrameStyle(QFrame.WinPanel | QFrame.Sunken)
+ cannon_box.setFrameStyle(QFrame.Shape.WinPanel | QFrame.Shadow.Sunken)
self._cannon_field = CannonField()
self._cannon_field.missed.connect(self.missed)
shoot = QPushButton("&Shoot")
- shoot.setFont(QFont("Times", 18, QFont.Bold))
+ shoot.setFont(QFont("Times", 18, QFont.Weight.Bold))
shoot.clicked.connect(self.fire)
self._cannon_field.can_shoot.connect(shoot.setEnabled)
restart = QPushButton("&New Game")
- restart.setFont(QFont("Times", 18, QFont.Bold))
+ restart.setFont(QFont("Times", 18, QFont.Weight.Bold))
restart.clicked.connect(self.new_game)
hits_label = QLabel("HITS")
shots_left_label = QLabel("SHOTS LEFT")
- QShortcut(QKeySequence(Qt.Key_Enter), self, self.fire)
- QShortcut(QKeySequence(Qt.Key_Return), self, self.fire)
- QShortcut(QKeySequence(Qt.CTRL | Qt.Key_Q), self, self.close)
+ QShortcut(QKeySequence(Qt.Key.Key_Enter), self, self.fire)
+ QShortcut(QKeySequence(Qt.Key.Key_Return), self, self.fire)
+ QShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Q), self, self.close)
top_layout = QHBoxLayout()
top_layout.addWidget(shoot)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
# PySide6 tutorial 2
quit = QPushButton("Quit")
quit.resize(75, 30)
- quit.setFont(QFont("Times", 18, QFont.Bold))
+ quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
quit.clicked.connect(app.quit)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
# PySide6 tutorial 3
window.resize(200, 120)
quit = QPushButton("Quit", window)
- quit.setFont(QFont("Times", 18, QFont.Bold))
+ quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
quit.setGeometry(10, 40, 180, 40)
quit.clicked.connect(app.quit)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
# PySide6 tutorial 4
self.quit = QPushButton("Quit", self)
self.quit.setGeometry(62, 40, 75, 30)
- self.quit.setFont(QFont("Times", 18, QFont.Bold))
+ self.quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
self.quit.clicked.connect(qApp.quit) # noqa: F821
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
# PySide6 tutorial 5
super().__init__(parent)
quit = QPushButton("Quit")
- quit.setFont(QFont("Times", 18, QFont.Bold))
+ quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
lcd = QLCDNumber(2)
- slider = QSlider(Qt.Horizontal)
+ slider = QSlider(Qt.Orientation.Horizontal)
slider.setRange(0, 99)
slider.setValue(0)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
# PySide6 tutorial 6
super().__init__(parent)
lcd = QLCDNumber(2)
- slider = QSlider(Qt.Horizontal)
+ slider = QSlider(Qt.Orientation.Horizontal)
slider.setRange(0, 99)
slider.setValue(0)
slider.valueChanged.connect(lcd.display)
super().__init__(parent)
quit = QPushButton("Quit")
- quit.setFont(QFont("Times", 18, QFont.Bold))
+ quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
quit.clicked.connect(qApp.quit) # noqa: F821
layout = QVBoxLayout(self)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
# PySide6 tutorial 7
lcd = QLCDNumber(2)
- self.slider = QSlider(Qt.Horizontal)
+ self.slider = QSlider(Qt.Orientation.Horizontal)
self.slider.setRange(0, 99)
self.slider.setValue(0)
super().__init__(parent)
quit = QPushButton("Quit")
- quit.setFont(QFont("Times", 18, QFont.Bold))
+ quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
quit.clicked.connect(qApp.quit) # noqa: F821
previous_range = None
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
# PySide6 tutorial 8
super().__init__(parent)
lcd = QLCDNumber(2)
- self.slider = QSlider(Qt.Horizontal)
+ self.slider = QSlider(Qt.Orientation.Horizontal)
self.slider.setRange(0, 99)
self.slider.setValue(0)
super().__init__(parent)
quit = QPushButton("Quit")
- quit.setFont(QFont("Times", 18, QFont.Bold))
+ quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
quit.clicked.connect(qApp.quit) # noqa: F821
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
# PySide6 tutorial 9
super().__init__(parent)
lcd = QLCDNumber(2)
- self.slider = QSlider(Qt.Horizontal)
+ self.slider = QSlider(Qt.Orientation.Horizontal)
self.slider.setRange(0, 99)
self.slider.setValue(0)
def paintEvent(self, event):
with QPainter(self) as painter:
- painter.setPen(Qt.NoPen)
- painter.setBrush(Qt.blue)
+ painter.setPen(Qt.PenStyle.NoPen)
+ painter.setBrush(Qt.GlobalColor.blue)
painter.translate(0, self.rect().height())
painter.drawPie(QRect(-35, -35, 70, 70), 0, 90 * 16)
super().__init__(parent)
quit = QPushButton("Quit")
- quit.setFont(QFont("Times", 18, QFont.Bold))
+ quit.setFont(QFont("Times", 18, QFont.Weight.Bold))
quit.clicked.connect(qApp.quit) # noqa: F821
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
def columnCount(self, parent=None):
return 3
- def data(self, index, role=Qt.DisplayRole):
- if role == Qt.DisplayRole:
+ def data(self, index, role=Qt.ItemDataRole.DisplayRole):
+ if role == Qt.ItemDataRole.DisplayRole:
row = index.row() + 1
column = index.column() + 1
return f"Row{row}, Column{column}"
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
return 3
#! [1]
- def data(self, index, role=Qt.DisplayRole):
+ def data(self, index, role=Qt.ItemDataRole.DisplayRole):
row = index.row()
col = index.column()
# generate a log message when this method gets called
print(f"row {row}, col{col}, role {role}")
- if role == Qt.DisplayRole:
+ if role == Qt.ItemDataRole.DisplayRole:
if row == 0 and col == 1:
return "<--left"
if row == 1 and col == 1:
return "right-->"
return f"Row{row}, Column{col + 1}"
- elif role == Qt.FontRole:
+ elif role == Qt.ItemDataRole.FontRole:
if row == 0 and col == 0: # change font only for cell(0,0)
bold_font = QFont()
bold_font.setBold(True)
return bold_font
- elif role == Qt.BackgroundRole:
+ elif role == Qt.ItemDataRole.BackgroundRole:
if row == 1 and col == 2: # change background only for cell(1,2)
- return QBrush(Qt.red)
+ return QBrush(Qt.GlobalColor.red)
- elif role == Qt.TextAlignmentRole:
+ elif role == Qt.ItemDataRole.TextAlignmentRole:
if row == 1 and col == 1: # change text alignment only for cell(1,1)
- return Qt.AlignRight | Qt.AlignVCenter
+ return Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter
- elif role == Qt.CheckStateRole:
+ elif role == Qt.ItemDataRole.CheckStateRole:
if row == 1 and col == 0: # add a checkbox to cell(1,0)
- return Qt.Checked
+ return Qt.CheckState.Checked
return None
#! [1]
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
return 3
#! [2]
- def data(self, index, role=Qt.DisplayRole):
+ def data(self, index, role=Qt.ItemDataRole.DisplayRole):
row = index.row()
col = index.column()
- if role == Qt.DisplayRole and row == 0 and col == 0:
+ if role == Qt.ItemDataRole.DisplayRole and row == 0 and col == 0:
return QTime.currentTime().toString()
return None
#! [2]
# we identify the top left cell
top_left = self.createIndex(0, 0)
# emit a signal to make the view reread identified data
- self.dataChanged.emit(top_left, top_left, [Qt.DisplayRole])
+ self.dataChanged.emit(top_left, top_left, [Qt.ItemDataRole.DisplayRole])
#! [3]
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
def columnCount(self, parent=None):
return 3
- def data(self, index, role=Qt.DisplayRole):
- if role == Qt.DisplayRole:
+ def data(self, index, role=Qt.ItemDataRole.DisplayRole):
+ if role == Qt.ItemDataRole.DisplayRole:
row = index.row() + 1
column = index.column() + 1
return f"Row{row}, Column{column}"
#! [1]
def headerData(self, section, orientation, role):
- if role == Qt.DisplayRole and orientation == Qt.Horizontal:
+ if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
return ["first", "second", "third"][section]
return None
#! [1]
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from itertools import chain
def columnCount(self, parent=None):
return COLS
- def data(self, index, role=Qt.DisplayRole):
- if role == Qt.DisplayRole and self.checkIndex(index):
+ def data(self, index, role=Qt.ItemDataRole.DisplayRole):
+ if role == Qt.ItemDataRole.DisplayRole and self.checkIndex(index):
return self._grid_data[index.row()][index.column()]
return None
#! [1]
def setData(self, index, value, role):
- if role != Qt.EditRole or not self.checkIndex(index):
+ if role != Qt.ItemDataRole.EditRole or not self.checkIndex(index):
return False
# save value from editor to member m_gridData
self._grid_data[index.row()][index.column()] = value
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
def selection_changed_slot(self, new_selection, old_selection):
# get the text of the selected item
index = self._tree_view.selectionModel().currentIndex()
- selected_text = index.data(Qt.DisplayRole)
+ selected_text = index.data(Qt.ItemDataRole.DisplayRole)
# find out the hierarchy level of the selected item
hierarchy_level = 1
seek_root = index
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from textwrap import dedent
QToolTip.showText(event.globalPosition().toPoint(), text, self)
def mousePressEvent(self, event):
- if event.button() == Qt.LeftButton:
+ if event.button() == Qt.MouseButton.LeftButton:
self._last_key = self._unicode_from_pos(event.position().toPoint())
if self._last_key != -1:
c = chr(self._last_key)
def render(self, event, painter):
painter = QPainter(self)
- painter.fillRect(event.rect(), QBrush(Qt.white))
+ painter.fillRect(event.rect(), QBrush(Qt.GlobalColor.white))
painter.setFont(self._display_font)
redraw_rect = event.rect()
begin_row = int(redraw_rect.top() / self._square_size)
end_row = int(redraw_rect.bottom() / self._square_size)
begin_column = int(redraw_rect.left() / self._square_size)
end_column = int(redraw_rect.right() / self._square_size)
- painter.setPen(QPen(Qt.gray))
+ painter.setPen(QPen(Qt.GlobalColor.gray))
for row in range(begin_row, end_row + 1):
for column in range(begin_column, end_column + 1):
x = int(column * self._square_size)
painter.drawRect(x, y, self._square_size, self._square_size)
font_metrics = QFontMetrics(self._display_font)
- painter.setPen(QPen(Qt.black))
+ painter.setPen(QPen(Qt.GlobalColor.black))
for row in range(begin_row, end_row + 1):
for column in range(begin_column, end_column + 1):
key = int(row * COLUMNS + column)
if key == self._last_key:
painter.fillRect(column * self._square_size + 1,
row * self._square_size + 1,
- self._square_size, self._square_size, QBrush(Qt.red))
+ self._square_size, self._square_size,
+ QBrush(Qt.GlobalColor.red))
text = chr(key)
painter.drawText(column * self._square_size + (self._square_size / 2)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Qt, qVersion, qFuzzyCompare
from PySide6.QtGui import QGuiApplication, QFontDatabase
text_edit.setReadOnly(True)
text_edit.setFont(QFontDatabase.systemFont(QFontDatabase.FixedFont))
main_layout.addWidget(text_edit)
- button_box = QDialogButtonBox(QDialogButtonBox.Close, self)
+ button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Close, self)
button_box.rejected.connect(self.reject)
main_layout.addWidget(button_box)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Qt, QSignalBlocker, Slot
from PySide6.QtGui import QGuiApplication, QClipboard, QFont, QFontDatabase
self._filter_label = QLabel("Filter:")
self._filter_combo = QComboBox()
- self._filter_combo.addItem("All", int(QFontComboBox.AllFonts.value))
- self._filter_combo.addItem("Scalable", int(QFontComboBox.ScalableFonts.value))
- self._filter_combo.addItem("Monospaced", int(QFontComboBox.MonospacedFonts.value))
- self._filter_combo.addItem("Proportional", int(QFontComboBox.ProportionalFonts.value))
+ self._filter_combo.addItem("All", QFontComboBox.FontFilter.AllFonts)
+ self._filter_combo.addItem("Scalable", QFontComboBox.FontFilter.ScalableFonts)
+ self._filter_combo.addItem("Monospaced", QFontComboBox.FontFilter.MonospacedFonts)
+ self._filter_combo.addItem("Proportional", QFontComboBox.FontFilter.ProportionalFonts)
self._filter_combo.setCurrentIndex(0)
self._filter_combo.currentIndexChanged.connect(self.filter_changed)
@Slot(int)
def filter_changed(self, f):
- filter = QFontComboBox.FontFilter(self._filter_combo.itemData(f))
+ filter = self._filter_combo.itemData(f)
self._font_combo.setFontFilters(filter)
count = self._font_combo.count()
self.statusBar().showMessage(f"{count} font(s) found")
screen_geometry = self.screen().geometry()
dialog = FontInfoDialog(self)
dialog.setWindowTitle("Fonts")
- dialog.setAttribute(Qt.WA_DeleteOnClose)
+ dialog.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
dialog.resize(screen_geometry.width() / 4, screen_geometry.height() / 4)
dialog.show()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtCore import QTime, QTimer, Slot
class DigitalClock(QLCDNumber):
def __init__(self, parent=None):
super().__init__(parent)
- self.setSegmentStyle(QLCDNumber.Filled)
+ self.setSegmentStyle(QLCDNumber.SegmentStyle.Filled)
self.setDigitCount(8)
self.timer = QTimer(self)
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/widgets/tetrix example from Qt v5.x"""
self.board = TetrixBoard()
next_piece_label = QLabel()
- next_piece_label.setFrameStyle(QFrame.Box | QFrame.Raised)
- next_piece_label.setAlignment(Qt.AlignCenter)
+ next_piece_label.setFrameStyle(QFrame.Shape.Box | QFrame.Shadow.Raised)
+ next_piece_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.board.set_next_piece_label(next_piece_label)
score_lcd = QLCDNumber(5)
- score_lcd.setSegmentStyle(QLCDNumber.Filled)
+ score_lcd.setSegmentStyle(QLCDNumber.SegmentStyle.Filled)
level_lcd = QLCDNumber(2)
- level_lcd.setSegmentStyle(QLCDNumber.Filled)
+ level_lcd.setSegmentStyle(QLCDNumber.SegmentStyle.Filled)
lines_lcd = QLCDNumber(5)
- lines_lcd.setSegmentStyle(QLCDNumber.Filled)
+ lines_lcd.setSegmentStyle(QLCDNumber.SegmentStyle.Filled)
start_button = QPushButton("&Start")
- start_button.setFocusPolicy(Qt.NoFocus)
+ start_button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
quit_button = QPushButton("&Quit")
- quit_button.setFocusPolicy(Qt.NoFocus)
+ quit_button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
pause_button = QPushButton("&Pause")
- pause_button.setFocusPolicy(Qt.NoFocus)
+ pause_button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
start_button.clicked.connect(self.board.start)
pause_button.clicked.connect(self.board.pause)
def create_label(self, text):
lbl = QLabel(text)
- lbl.setAlignment(Qt.AlignHCenter | Qt.AlignBottom)
+ lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignBottom)
return lbl
self.level = 0
self.board = None
- self.setFrameStyle(QFrame.Panel | QFrame.Sunken)
- self.setFocusPolicy(Qt.StrongFocus)
+ self.setFrameStyle(QFrame.Shape.Panel | QFrame.Shadow.Sunken)
+ self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
self._is_started = False
self._is_paused = False
self.clear_board()
rect = self.contentsRect()
if self._is_paused:
- painter.drawText(rect, Qt.AlignCenter, "Pause")
+ painter.drawText(rect, Qt.AlignmentFlag.AlignCenter, "Pause")
return
board_top = rect.bottom() - TetrixBoard.board_height * self.square_height()
return
key = event.key()
- if key == Qt.Key_Left:
+ if key == Qt.Key.Key_Left:
self.try_move(self._cur_piece, self._cur_x - 1, self._cur_y)
- elif key == Qt.Key_Right:
+ elif key == Qt.Key.Key_Right:
self.try_move(self._cur_piece, self._cur_x + 1, self._cur_y)
- elif key == Qt.Key_Down:
+ elif key == Qt.Key.Key_Down:
self.try_move(self._cur_piece.rotated_right(), self._cur_x, self._cur_y)
- elif key == Qt.Key_Up:
+ elif key == Qt.Key.Key_Up:
self.try_move(self._cur_piece.rotated_left(), self._cur_x, self._cur_y)
- elif key == Qt.Key_Space:
+ elif key == Qt.Key.Key_Space:
self.drop_down()
- elif key == Qt.Key_D:
+ elif key == Qt.Key.Key_D:
self.one_line_down()
else:
super(TetrixBoard, self).keyPressEvent(event)
y + self.square_height() - 1, x + self.square_width() - 1, y + 1)
-class TetrixPiece(object):
+class TetrixPiece:
coords_table = (
((0, 0), (0, 0), (0, 0), (0, 0)),
((0, -1), (0, 0), (-1, 0), (-1, 1)),
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the widgets/gallery example from Qt v5.15"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
disable_widgets_checkbox.toggled.connect(simple_input_widgets_groupbox.setDisabled)
help_shortcut = QShortcut(self)
- help_shortcut.setKey(QKeySequence.HelpContents)
+ help_shortcut.setKey(QKeySequence.StandardKey.HelpContents)
help_shortcut.activated.connect(self.help_on_current_widget)
top_layout = QHBoxLayout()
top_layout.addStretch(1)
top_layout.addWidget(disable_widgets_checkbox)
- dialog_buttonbox = QDialogButtonBox(QDialogButtonBox.Help
- | QDialogButtonBox.Close)
+ dialog_buttonbox = QDialogButtonBox(QDialogButtonBox.StandardButton.Help
+ | QDialogButtonBox.StandardButton.Close)
init_widget(dialog_buttonbox, "dialogButtonBox")
dialog_buttonbox.helpRequested.connect(launch_module_help)
dialog_buttonbox.rejected.connect(self.reject)
init_widget(menu_toolbutton, "menuButton")
menu_toolbutton.setText("Menu Button")
tool_menu = QMenu(menu_toolbutton)
- menu_toolbutton.setPopupMode(QToolButton.InstantPopup)
+ menu_toolbutton.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
tool_menu.addAction("Option")
tool_menu.addSeparator()
action = tool_menu.addAction("Checkable Option")
checkbox = QCheckBox("Tri-state check box")
init_widget(checkbox, "checkBox")
checkbox.setTristate(True)
- checkbox.setCheckState(Qt.PartiallyChecked)
+ checkbox.setCheckState(Qt.CheckState.PartiallyChecked)
checkable_layout = QVBoxLayout()
checkable_layout.addWidget(radiobutton_1)
def create_itemview_tabwidget(self):
result = QTabWidget()
init_widget(result, "bottomLeftTabWidget")
- result.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Ignored)
+ result.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Ignored)
tree_view = QTreeView()
init_widget(tree_view, "treeView")
icon_mode_listview = QListView()
init_widget(icon_mode_listview, "iconModeListView")
- icon_mode_listview.setViewMode(QListView.IconMode)
+ icon_mode_listview.setViewMode(QListView.ViewMode.IconMode)
icon_mode_listview.setModel(list_model)
result.addTab(embed_into_hbox_layout(tree_view), "Tree View")
lineedit = QLineEdit("s3cRe7")
init_widget(lineedit, "lineEdit")
lineedit.setClearButtonEnabled(True)
- lineedit.setEchoMode(QLineEdit.Password)
+ lineedit.setEchoMode(QLineEdit.EchoMode.Password)
spin_box = QSpinBox()
init_widget(spin_box, "spinBox")
slider = QSlider()
init_widget(slider, "slider")
- slider.setOrientation(Qt.Horizontal)
+ slider.setOrientation(Qt.Orientation.Horizontal)
slider.setValue(40)
scrollbar = QScrollBar()
init_widget(scrollbar, "scrollBar")
- scrollbar.setOrientation(Qt.Horizontal)
+ scrollbar.setOrientation(Qt.Orientation.Horizontal)
scrollbar.setValue(60)
dial = QDial()
# Copyright (C) 2013 Riverbank Computing Limited.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""PySide6 port of the xml/dombookmarks example from Qt v5.x"""
return
in_file = QFile(file_name)
- if not in_file.open(QFile.ReadOnly | QFile.Text):
+ if not in_file.open(QFile.OpenModeFlag.ReadOnly | QFile.OpenModeFlag.Text):
reason = in_file.errorString()
QMessageBox.warning(self, "DOM Bookmarks",
f"Cannot read file {file_name}:\n{reason}.")
return
out_file = QFile(file_name)
- if not out_file.open(QFile.WriteOnly | QFile.Text):
+ if not out_file.open(QFile.OpenModeFlag.WriteOnly | QFile.Text):
reason = out_file.errorString()
QMessageBox.warning(self, "DOM Bookmarks",
f"Cannot write file {file_name}:\n{reason}.")
def create_menus(self):
self._file_menu = self.menuBar().addMenu("&File")
self._file_menu.addAction(QAction("&Open...", self,
- shortcut=QKeySequence(
- Qt.CTRL | Qt.Key_O), triggered=self.open))
+ shortcut=QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_O),
+ triggered=self.open))
self._file_menu.addAction(QAction("&Save As...", self,
- shortcut=QKeySequence(
- Qt.CTRL | Qt.Key_S), triggered=self.save_as))
+ shortcut=QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_S),
+ triggered=self.save_as))
self._file_menu.addAction(QAction("E&xit", self,
- shortcut=QKeySequence(
- Qt.CTRL | Qt.Key_Q), triggered=self.close))
+ shortcut=QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Q),
+ triggered=self.close))
self.menuBar().addSeparator()
def __init__(self, parent=None):
super().__init__(parent)
- self.header().setSectionResizeMode(QHeaderView.Stretch)
+ self.header().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
self.setHeaderLabels(("Title", "Location"))
self._dom_document = QDomDocument()
self._folder_icon = QIcon()
self._bookmark_icon = QIcon()
- self._folder_icon.addPixmap(self.style().standardPixmap(QStyle.SP_DirClosedIcon),
- QIcon.Normal, QIcon.Off)
- self._folder_icon.addPixmap(self.style().standardPixmap(QStyle.SP_DirOpenIcon),
- QIcon.Normal, QIcon.On)
- self._bookmark_icon.addPixmap(self.style().standardPixmap(QStyle.SP_FileIcon))
+ style = self.style()
+ self._folder_icon.addPixmap(style.standardPixmap(QStyle.StandardPixmap.SP_DirClosedIcon),
+ QIcon.Mode.Normal, QIcon.State.Off)
+ self._folder_icon.addPixmap(style.standardPixmap(QStyle.StandardPixmap.SP_DirOpenIcon),
+ QIcon.Mode.Normal, QIcon.State.On)
+ self._bookmark_icon.addPixmap(style.standardPixmap(QStyle.StandardPixmap.SP_FileIcon))
def read(self, device):
ok, errorStr, errorLine, errorColumn = self._dom_document.setContent(device, True)
if not title:
title = "Folder"
- item.setFlags(item.flags() | Qt.ItemIsEditable)
+ item.setFlags(item.flags() | Qt.ItemFlag.ItemIsEditable)
item.setIcon(0, self._folder_icon)
item.setText(0, title)
if not title:
title = "Folder"
- child_item.setFlags(item.flags() | Qt.ItemIsEditable)
+ child_item.setFlags(item.flags() | Qt.ItemFlag.ItemIsEditable)
child_item.setIcon(0, self._bookmark_icon)
child_item.setText(0, title)
child_item.setText(1, child.attribute('href'))
elif child.tagName() == 'separator':
child_item = self.create_item(child, item)
- child_item.setFlags(item.flags() & ~(Qt.ItemIsSelectable | Qt.ItemIsEditable))
+ child_item.setFlags(item.flags() & ~(Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEditable)) # noqa: E501
child_item.setText(0, 30 * "\xb7")
child = child.nextSiblingElement()
--- /dev/null
+pip>=24.2
+setuptools==72.1.0
+importlib_metadata>=6
+importlib_resources>=5.10.2
+packaging>=24
+ordered-set>=3.1.1
+more_itertools>=8.8
+jaraco.text>=3.7
+importlib_metadata>=6
+tomli>=2.0.1
+wheel>=0.43.0
+platformdirs >= 2.6.2
-sphinx==7.2.6
-sphinx-design==0.5.0
+sphinx==7.4.7
+sphinx-design==0.6.0
sphinx-copybutton==0.5.2
-sphinx-tags==0.3.1
-sphinx-toolbox
-myst-parser==2.0.0
+sphinx-tags==0.4
+sphinx-toolbox==3.7.0
+sphinx-reredirects==0.1.5
+myst-parser==3.0.1
# FIXME: Using fork in order to enable the 'collapse_navbar=True'
# option for the sphinx-theme. Upstream proposal:
# https://github.com/pradyunsg/furo/pull/748#issuecomment-1895448722
# furo==2023.9.10
furo @ git+https://github.com/cmaureir/furo@add_collapse
-graphviz==0.20
+graphviz==0.20.3
# Build dependencies
-setuptools==69.1.1
-packaging==23.2
-build==1.0.3
-wheel==0.42.0
+setuptools==72.1.0
+packaging==24.1
+build==1.2.1
+wheel==0.43.0
distro==1.9.0; sys_platform == 'linux'
patchelf==0.17.2; sys_platform == 'linux'
-# 1.24.4 is the last version that supports Python 3.8
-numpy<1.25; python_version < '3.9'
-numpy==1.26.3; python_version >= '3.9'
-
-# For examples
-PyOpenGL
+# 2.0.2 is the last version that supports Python 3.9
+numpy<=2.0.2; python_version <= '3.9'
+numpy==2.1.3; python_version > '3.9'
+mypy[faster-cache]>=1.14.0
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
This is a setuptools setup-script for the Qt for Python project.
${CMAKE_CURRENT_SOURCE_DIR}/requirements-android.txt)
set(directories ${CMAKE_CURRENT_SOURCE_DIR}/deploy_lib
- ${CMAKE_CURRENT_SOURCE_DIR}/project)
+ ${CMAKE_CURRENT_SOURCE_DIR}/project_lib)
if(NOT NO_QT_TOOLS STREQUAL "yes")
set(TOOLS_PATH "${QT6_INSTALL_PREFIX}/${QT6_HOST_INFO_BINDIR}")
"${TOOLS_PATH}/qmlls${CMAKE_EXECUTABLE_SUFFIX}"
"${TOOLS_PATH}/qsb${CMAKE_EXECUTABLE_SUFFIX}"
"${TOOLS_PATH}/balsam${CMAKE_EXECUTABLE_SUFFIX}"
- "${TOOLS_PATH}/balsamui${CMAKE_EXECUTABLE_SUFFIX}")
+ "${TOOLS_PATH}/balsamui${CMAKE_EXECUTABLE_SUFFIX}"
+ "${TOOLS_PATH}/svgtoqml${CMAKE_EXECUTABLE_SUFFIX}")
if (APPLE)
list(APPEND directories "${TOOLS_PATH}/Assistant.app"
# pyside6-rcc, pyside6-uic, pyside6-designer, shiboken and pyside6-lupdate entrypoints
foreach(file ${files})
if(EXISTS ${file})
+ message(STATUS "Installing tool: ${file}")
install(FILES "${file}"
DESTINATION bin
PERMISSIONS
FILE_PERMISSIONS
OWNER_EXECUTE OWNER_WRITE OWNER_READ
GROUP_EXECUTE GROUP_READ
- WORLD_EXECUTE WORLD_READ)
+ WORLD_EXECUTE WORLD_READ
+ PATTERN "android_utilities.py" EXCLUDE) # excluding the symlink
endforeach()
+
+ # dealing with android_utilities.py
+ set(ANDROID_UTILITIES_REALPATH
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../tools/cross_compile_android/android_utilities.py")
+ message(STATUS "Installing ${ANDROID_UTILITIES_REALPATH}")
+ install(FILES "${ANDROID_UTILITIES_REALPATH}"
+ DESTINATION bin/deploy_lib/android
+ PERMISSIONS
+ OWNER_EXECUTE OWNER_WRITE OWNER_READ
+ GROUP_EXECUTE GROUP_READ
+ WORLD_EXECUTE WORLD_READ)
endif()
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+import sys
import argparse
import logging
import shutil
if config_file_exists:
logging.info(f"[DEPLOY] Using existing config file {config_file}")
else:
- config_file = create_config_file(dry_run=dry_run, config_file=config_file,
- main_file=main_file)
+ config_file = create_config_file(main_file=main_file, dry_run=dry_run)
config = AndroidConfig(config_file=config_file, source_file=main_file,
python_exe=python.exe, dry_run=dry_run, android_data=android_data,
existing_config_file=config_file_exists,
- extra_ignore_dirs=extra_ignore_dirs)
+ extra_ignore_dirs=extra_ignore_dirs, name=name)
if not config.wheel_pyside and not config.wheel_shiboken:
raise RuntimeError(f"[DEPLOY] No PySide{MAJOR_VERSION} and Shiboken{MAJOR_VERSION} wheels"
python.install_dependencies(config=config, packages="android_packages", is_android=True)
- # set application name
- if name:
- config.title = name
-
try:
config.modules += list(set(extra_modules).difference(set(config.modules)))
# this cannot be done when config file is initialized because cleanup() removes it
# so this can only be done after the cleanup()
- config.find_and_set_jars_dir()
- config.verify_and_set_recipe_dir()
+ config.jars_dir = config.find_jars_dir()
+ config.recipe_dir = config.find_recipe_dir()
# TODO: include qml files from pysidedeploy.spec rather than from extensions
# buildozer currently includes all the files with .qml extension
required=not config_option_exists())
parser.add_argument("--ndk-path", type=lambda p: Path(p).resolve(),
- help=("Path to Android NDK. If omitted, the tool's cache at "
- ".pyside6_android_deploy is checked to find the NDK")
+ help=("Path to Android NDK. The required version is r26b."
+ "If not provided, the tool will check its cache at "
+ ".pyside6_android_deploy to find the NDK.")
)
parser.add_argument("--sdk-path", type=lambda p: Path(p).resolve(),
args = parser.parse_args()
+ # check if the Python version is greater than 3.12
+ if sys.version_info >= (3, 12):
+ raise RuntimeError("[DEPLOY] Android deployment requires Python version 3.11 or lower. "
+ "This is due to a restriction in buildozer.")
+
main(args.name, args.wheel_pyside, args.wheel_shiboken, args.ndk_path, args.sdk_path,
args.config_file, args.init, args.loglevel, args.dry_run, args.keep_deployment_files,
args.force, args.extra_ignore_dirs, args.extra_modules)
${android_main_srcs}/QtService.java
)
# set android.jar from the sdk, for compiling the java files into .jar
- set(sdk_jar_location "${ANDROID_SDK_ROOT}/platforms/${ANDROID_PLATFORM}/android.jar")
+ set(sdk_jar_location "${ANDROID_SDK_ROOT}/platforms/android-${CMAKE_ANDROID_API}/android.jar")
+ file(GLOB sources_list LIST_DIRECTORIES true "${ANDROID_SDK_ROOT}/platforms/android-${CMAKE_ANDROID_API}/*")
if (NOT EXISTS "${sdk_jar_location}")
- message(FATAL_ERROR "Could not locate Android SDK jar for api '${api}'")
+ message(FATAL_ERROR "Could not locate Android SDK jar for api '${CMAKE_ANDROID_API}' - ${sdk_jar_location}")
endif()
# this variable is accessed by qt_internal_add_jar
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
""" pyside6-deploy deployment tool
"""
+import sys
import argparse
import logging
import traceback
Linux = .bin
""")
+HELP_MODE = dedent("""
+ The mode in which the application is deployed. The options are: onefile,
+ standalone. The default value is onefile.
+
+ This options translates to the mode Nuitka uses to create the executable.
+
+ macOS by default uses the --standalone option.
+ """)
+
def main(main_file: Path = None, name: str = None, config_file: Path = None, init: bool = False,
loglevel=logging.WARNING, dry_run: bool = False, keep_deployment_files: bool = False,
- force: bool = False, extra_ignore_dirs: str = None, extra_modules_grouped: str = None):
+ force: bool = False, extra_ignore_dirs: str = None, extra_modules_grouped: str = None,
+ mode: str = None):
logging.basicConfig(level=loglevel)
+
+ # in case pyside6-deploy is run from a completely different location than the project
+ # directory
+ if main_file and main_file.exists():
+ config_file = main_file.parent / "pysidedeploy.spec"
+
if config_file and not config_file.exists() and not main_file.exists():
raise RuntimeError(dedent("""
Directory does not contain main.py file.
extra_modules.append(extra_module)
python = PythonExecutable(dry_run=dry_run, init=init, force=force)
- config_file_exists = config_file and Path(config_file).exists()
+ config_file_exists = config_file and config_file.exists()
if config_file_exists:
logging.info(f"[DEPLOY] Using existing config file {config_file}")
else:
- config_file = create_config_file(dry_run=dry_run, config_file=config_file,
- main_file=main_file)
+ config_file = create_config_file(main_file=main_file, dry_run=dry_run, )
config = DesktopConfig(config_file=config_file, source_file=main_file, python_exe=python.exe,
dry_run=dry_run, existing_config_file=config_file_exists,
- extra_ignore_dirs=extra_ignore_dirs)
-
- # set application name
- if name:
- config.title = name
+ extra_ignore_dirs=extra_ignore_dirs, mode=mode, name=name)
cleanup(config=config)
config.modules += list(set(extra_modules).difference(set(config.modules)))
- # writing config file
- # in the case of --dry-run, we use default.spec as reference. Do not save the changes
- # for --dry-run
+ # Do not save the config changes if --dry-run is specified
if not dry_run:
config.update_config()
if config.qml_files:
- logging.info(f"[DEPLOY] Included QML files: {config.qml_files}")
+ logging.info("[DEPLOY] Included QML files: "
+ f"{[str(qml_file) for qml_file in config.qml_files]}")
if init:
# config file created above. Exiting.
logging.info(f"[DEPLOY]: Config file {config.config_file} created")
return
+ # If modules contain QtSql and the platform is macOS, then pyside6-deploy will not work
+ # currently. The fix ideally will have to come from Nuitka.
+ # See PYSIDE-2835
+ # TODO: Remove this check once the issue is fixed in Nuitka
+ # Nuitka Issue: https://github.com/Nuitka/Nuitka/issues/3079
+ if "Sql" in config.modules and sys.platform == "darwin":
+ print("[DEPLOY] QtSql Application is not supported on macOS with pyside6-deploy")
+ return
+
try:
# create executable
if not dry_run:
excluded_qml_plugins=config.excluded_qml_plugins,
icon=config.icon,
dry_run=dry_run,
- permissions=config.permissions)
+ permissions=config.permissions,
+ mode=config.mode)
except Exception:
print(f"[DEPLOY] Exception occurred: {traceback.format_exc()}")
finally:
- if config.generated_files_path and config:
- finalize(config=config)
+ if config.generated_files_path:
+ if not dry_run:
+ finalize(config=config)
if not keep_deployment_files:
cleanup(config=config)
parser.add_argument("--extra-modules", type=str, help=HELP_EXTRA_MODULES)
+ parser.add_argument("--mode", choices=["onefile", "standalone"], default="onefile",
+ help=HELP_MODE)
+
args = parser.parse_args()
main(args.main_file, args.name, args.config_file, args.init, args.loglevel, args.dry_run,
- args.keep_deployment_files, args.force, args.extra_ignore_dirs, args.extra_modules)
+ args.keep_deployment_files, args.force, args.extra_ignore_dirs, args.extra_modules,
+ args.mode)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import sys
from pathlib import Path
from textwrap import dedent
EXE_FORMAT = ".bin"
DEFAULT_APP_ICON = str((Path(__file__).parent / f"pyside_icon{IMAGE_FORMAT}").resolve())
+DEFAULT_IGNORE_DIRS = {"site-packages", "deployment", ".git", ".qtcreator", "build", "dist",
+ "tests", "doc", "docs", "examples", ".vscode", "__pycache__"}
+
IMPORT_WARNING_PYSIDE = (f"[DEPLOY] Found 'import PySide6' in file {0}"
". Use 'from PySide6 import <module>' or pass the module"
" needed using --extra-modules command line argument")
Example usage 2: --extra-modules=QtNetwork,QtSvg
""")
+# plugins to be removed from the --include-qt-plugins option because these plugins
+# don't exist in site-package under PySide6/Qt/plugins
+PLUGINS_TO_REMOVE = ["accessiblebridge", "platforms/darwin", "networkaccess", "scenegraph"]
+
def get_all_pyside_modules():
"""
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+from pathlib import Path
# maps instruction set to Android platform names
platform_map = {"aarch64": "arm64-v8a",
"armeabi-v7a": "armeabi-v7a",
"x86": "x86"}
+ANDROID_DEPLOY_CACHE = Path.home() / ".pyside6_android_deploy"
+
from .android_helper import (create_recipe, extract_and_copy_jar, get_wheel_android_arch,
AndroidData, get_llvm_readobj, find_lib_dependencies,
find_qtlibs_in_wheel)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import re
+import sys
import tempfile
import logging
import zipfile
import xml.etree.ElementTree as ET
-from typing import List
from pathlib import Path
from pkginfo import Wheel
from . import (extract_and_copy_jar, get_wheel_android_arch, find_lib_dependencies,
- get_llvm_readobj, find_qtlibs_in_wheel, platform_map, create_recipe)
-from .. import (Config, find_pyside_modules, get_all_pyside_modules, MAJOR_VERSION)
-
-ANDROID_NDK_VERSION = "26b"
-ANDROID_DEPLOY_CACHE = Path.home() / ".pyside6_android_deploy"
+ get_llvm_readobj, find_qtlibs_in_wheel, platform_map, create_recipe,
+ ANDROID_DEPLOY_CACHE)
+from .. import (Config, get_all_pyside_modules, MAJOR_VERSION)
+from .android_utilities import (ANDROID_NDK_VERSION, ANDROID_NDK_VERSION_NUMBER_SUFFIX,
+ download_android_ndk)
class AndroidConfig(Config):
"""
def __init__(self, config_file: Path, source_file: Path, python_exe: Path, dry_run: bool,
android_data, existing_config_file: bool = False,
- extra_ignore_dirs: List[str] = None):
+ extra_ignore_dirs: list[str] = None, name: str = None):
super().__init__(config_file=config_file, source_file=source_file, python_exe=python_exe,
- dry_run=dry_run, existing_config_file=existing_config_file)
+ dry_run=dry_run, existing_config_file=existing_config_file, name=name)
self.extra_ignore_dirs = extra_ignore_dirs
if android_data.ndk_path:
# from cli
self.ndk_path = android_data.ndk_path
- else:
+ elif not existing_config_file:
# from config
ndk_path_temp = self.get_value("buildozer", "ndk_path")
if ndk_path_temp:
else:
ndk_path_temp = (ANDROID_DEPLOY_CACHE / "android-ndk"
/ f"android-ndk-r{ANDROID_NDK_VERSION}")
+ if sys.platform == "darwin":
+ ndk_path_temp = (
+ ANDROID_DEPLOY_CACHE / "android-ndk"
+ / f"AndroidNDK{ANDROID_NDK_VERSION_NUMBER_SUFFIX}.app/Contents/NDK"
+ )
if ndk_path_temp.exists():
self.ndk_path = ndk_path_temp
+ else:
+ # download NDK
+ if not ANDROID_DEPLOY_CACHE.exists():
+ ANDROID_DEPLOY_CACHE.mkdir()
+ logging.info(f"Cache created at {str(ANDROID_DEPLOY_CACHE.resolve())}")
- if self.ndk_path:
- print(f"Using Android NDK: {str(self.ndk_path)}")
- else:
- raise FileNotFoundError("[DEPLOY] Unable to find Android NDK. Please pass the NDK "
- "path either from the CLI or from pysidedeploy.spec")
+ logging.info("[DEPLOY] Downloading Android NDK")
+ self.ndk_path = download_android_ndk(ANDROID_DEPLOY_CACHE)
self.sdk_path = None
if android_data.sdk_path:
if jars_dir_temp and Path(jars_dir_temp).resolve().exists():
self.jars_dir = Path(jars_dir_temp).resolve()
- self._arch = None
- if self.get_value("buildozer", "arch"):
- self.arch = self.get_value("buildozer", "arch")
+ android_arch = self.get_value("buildozer", "arch")
+ if android_arch:
+ self._arch = android_arch
else:
- self._find_and_set_arch()
+ self.arch = self._find_arch()
# maps to correct platform name incase the instruction set was specified
self._arch = platform_map[self.arch]
self.qt_libs_path: zipfile.Path = find_qtlibs_in_wheel(wheel_pyside=self.wheel_pyside)
logging.info(f"[DEPLOY] Qt libs path inside wheel: {str(self.qt_libs_path)}")
- if self.get_value("qt", "modules"):
- self.modules = self.get_value("qt", "modules").split(",")
+ modls = self.get_value("qt", "modules")
+ if modls:
+ self._modules = modls.split(",")
else:
- self._find_and_set_pysidemodules()
- self._find_and_set_qtquick_modules()
- self.modules += self._find_dependent_qt_modules()
+ modls = self._find_pysidemodules()
+ modls += self._find_qtquick_modules()
+ modls += self._find_dependent_qt_modules(modules=modls)
# remove duplicates
- self.modules = list(set(self.modules))
+ self.modules = list(set(modls))
# gets the xml dependency files from Qt installation path
- self._dependency_files = []
- self._find_and_set_dependency_files()
+ self.dependency_files = self._find_dependency_files()
dependent_plugins = []
self._local_libs = []
- if self.get_value("buildozer", "local_libs"):
- self._local_libs = self.get_value("buildozer", "local_libs").split(",")
+ loc_libs = self.get_value("buildozer", "local_libs")
+ if loc_libs:
+ self._local_libs = loc_libs.split(",")
else:
# the local_libs can also store dependent plugins
local_libs, dependent_plugins = self._find_local_libs()
self.local_libs = list(set(local_libs))
self._qt_plugins = []
- if self.get_value("android", "plugins"):
- self._qt_plugins = self.get_value("android", "plugins").split(",")
+ qt_plgns = self.get_value("android", "plugins")
+ if qt_plgns:
+ self._qt_plugins = qt_plgns.split(",")
elif dependent_plugins:
self._find_plugin_dependencies(dependent_plugins)
self.qt_plugins = list(set(dependent_plugins))
def dependency_files(self, dependency_files):
self._dependency_files = dependency_files
- def _find_and_set_pysidemodules(self):
- self.modules = find_pyside_modules(project_dir=self.project_dir,
- extra_ignore_dirs=self.extra_ignore_dirs,
- project_data=self.project_data)
- logging.info("The following PySide modules were found from the python files of "
- f"the project {self.modules}")
-
- def find_and_set_jars_dir(self):
+ def find_jars_dir(self):
"""Extract out and copy .jar files to {generated_files_path}
"""
+ jars_dir = None
if not self.dry_run:
logging.info("[DEPLOY] Extract and copy jar files from PySide6 wheel to "
f"{self.generated_files_path}")
- self.jars_dir = extract_and_copy_jar(wheel_path=self.wheel_pyside,
- generated_files_path=self.generated_files_path)
+ jars_dir = extract_and_copy_jar(wheel_path=self.wheel_pyside,
+ generated_files_path=self.generated_files_path)
- def _find_and_set_arch(self):
+ return jars_dir
+
+ def _find_arch(self):
"""Find architecture from wheel name
"""
- self.arch = get_wheel_android_arch(wheel=self.wheel_pyside)
- if not self.arch:
+ arch = get_wheel_android_arch(wheel=self.wheel_pyside)
+ if not arch:
raise RuntimeError("[DEPLOY] PySide wheel corrupted. Wheel name should end with"
"platform name")
- def _find_dependent_qt_modules(self):
+ return arch
+
+ def _find_dependent_qt_modules(self, modules: list[str]) -> list[str]:
"""
Given pysidedeploy_config.modules, find all the other dependent Qt modules. This is
done by using llvm-readobj (readelf) to find the dependent libraries from the module
archive.extractall(tmpdir)
qt_libs_tmpdir = Path(tmpdir) / lib_path_suffix
# find the lib folder where Qt libraries are stored
- for module_name in sorted(self.modules):
+ for module_name in sorted(modules):
qt_module_path = qt_libs_tmpdir / f"libQt6{module_name}_{self.arch}.so"
if not qt_module_path.exists():
raise FileNotFoundError(f"[DEPLOY] libQt6{module_name}_{self.arch}.so not found"
match = lib_pattern.search(dependency)
if match:
module = match.group("mod_name")
- if module not in self.modules:
+ if module not in modules:
dependent_modules.add(module)
# check if the PySide6 binary for the Qt module actually exists
return dependent_modules
- def _find_and_set_dependency_files(self) -> List[zipfile.Path]:
+ def _find_dependency_files(self) -> list[zipfile.Path]:
"""
Based on `modules`, returns the Qt6{module}_{arch}-android-dependencies.xml file, which
contains the various dependencies of the module, like permissions, plugins etc
"""
needed_dependency_files = [(f"Qt{MAJOR_VERSION}{module}_{self.arch}"
"-android-dependencies.xml") for module in self.modules]
-
+ found_dependency_files = []
for dependency_file_name in needed_dependency_files:
dependency_file = self.qt_libs_path / dependency_file_name
if dependency_file.exists():
- self._dependency_files.append(dependency_file)
+ found_dependency_files.append(dependency_file)
logging.info("[DEPLOY] The following dependency files were found: "
- f"{*self._dependency_files,}")
+ f"{*found_dependency_files, }")
+
+ return found_dependency_files
def _find_local_libs(self):
local_libs = set()
return list(local_libs), list(plugins)
- def _find_plugin_dependencies(self, dependent_plugins: List[str]):
+ def _find_plugin_dependencies(self, dependent_plugins: list[str]):
# The `bundled` element in the dependency xml files points to the folder where
# additional dependencies for the application exists. Inspecting the depenency files
# in android, this always points to the specific Qt plugin dependency folder.
if plugin_infix_name not in dependent_plugins:
dependent_plugins.append(plugin_infix_name)
- def verify_and_set_recipe_dir(self):
+ def find_recipe_dir(self):
# create recipes
# https://python-for-android.readthedocs.io/en/latest/recipes/
# These recipes are manually added through buildozer.spec file to be used by
# python_for_android while building the distribution
+ recipe_dir = None
if not self.recipes_exist() and not self.dry_run:
logging.info("[DEPLOY] Creating p4a recipes for PySide6 and shiboken6")
version = Wheel(self.wheel_pyside).version
create_recipe(version=version, component=f"shiboken{MAJOR_VERSION}",
wheel_path=self.wheel_shiboken,
generated_files_path=self.generated_files_path)
- self.recipe_dir = ((self.generated_files_path
- / "recipes").resolve())
+ recipe_dir = ((self.generated_files_path
+ / "recipes").resolve())
+ return recipe_dir
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
+from __future__ import annotations
+import sys
import logging
import zipfile
from dataclasses import dataclass
from pathlib import Path
-from typing import List, Set
from zipfile import ZipFile
from jinja2 import Environment, FileSystemLoader
def create_recipe(version: str, component: str, wheel_path: str, generated_files_path: Path,
- qt_modules: List[str] = None, local_libs: List[str] = None,
- plugins: List[str] = None):
+ qt_modules: list[str] = None, local_libs: list[str] = None,
+ plugins: list[str] = None):
'''
Create python_for_android recipe for PySide6 and shiboken6
'''
'''
# TODO: Requires change if Windows platform supports Android Deployment or if we
# support host other than linux-x86_64
- return (ndk_path / "toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-readobj")
+ return (ndk_path / f"toolchains/llvm/prebuilt/{sys.platform}-x86_64/bin/llvm-readobj")
-def find_lib_dependencies(llvm_readobj: Path, lib_path: Path, used_dependencies: Set[str] = None,
+def find_lib_dependencies(llvm_readobj: Path, lib_path: Path, used_dependencies: set[str] = None,
dry_run: bool = False):
"""
Find all the Qt dependencies of a library using llvm_readobj
--- /dev/null
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+import logging
+import shutil
+import re
+import os
+import stat
+import sys
+import subprocess
+
+from urllib import request
+from pathlib import Path
+from packaging import version
+from tqdm import tqdm
+
+# the tag number does not matter much since we update the sdk later
+DEFAULT_SDK_TAG = 6514223
+ANDROID_NDK_VERSION = "26b"
+ANDROID_NDK_VERSION_NUMBER_SUFFIX = "10909125"
+
+
+def run_command(command: list[str], cwd: str | None = None, ignore_fail: bool = False,
+ dry_run: bool = False, accept_prompts: bool = False, show_stdout: bool = False,
+ capture_stdout: bool = False):
+
+ if capture_stdout and not show_stdout:
+ raise RuntimeError("capture_stdout should always be used together with show_stdout")
+
+ if dry_run:
+ print(" ".join(command))
+ return
+
+ input = None
+ if accept_prompts:
+ input = str.encode("y")
+
+ if show_stdout:
+ stdout = None
+ else:
+ stdout = subprocess.DEVNULL
+
+ result = subprocess.run(command, cwd=cwd, input=input, stdout=stdout,
+ capture_output=capture_stdout)
+
+ if result.returncode != 0 and not ignore_fail:
+ sys.exit(result.returncode)
+
+ if capture_stdout and not result.returncode:
+ return result.stdout.decode("utf-8")
+
+ return None
+
+
+class DownloadProgressBar(tqdm):
+ def update_to(self, b=1, bsize=1, tsize=None):
+ if tsize is not None:
+ self.total = tsize
+ self.update(b * bsize - self.n)
+
+
+class SdkManager:
+ def __init__(self, android_sdk_dir: Path, dry_run: bool = False):
+ self._sdk_manager = android_sdk_dir / "tools" / "bin" / "sdkmanager"
+
+ if not self._sdk_manager.exists():
+ raise RuntimeError(f"Unable to find SdkManager in {str(self._sdk_manager)}")
+
+ if not os.access(self._sdk_manager, os.X_OK):
+ current_permissions = stat.S_IMODE(os.lstat(self._sdk_manager).st_mode)
+ os.chmod(self._sdk_manager, current_permissions | stat.S_IEXEC)
+
+ self._android_sdk_dir = android_sdk_dir
+ self._dry_run = dry_run
+
+ def list_packages(self):
+ command = [self._sdk_manager, f"--sdk_root={self._android_sdk_dir}", "--list"]
+ return run_command(command=command, dry_run=self._dry_run, show_stdout=True,
+ capture_stdout=True)
+
+ def install(self, *args, accept_license: bool = False, show_stdout=False):
+ command = [str(self._sdk_manager), f"--sdk_root={self._android_sdk_dir}", *args]
+ run_command(command=command, dry_run=self._dry_run,
+ accept_prompts=accept_license, show_stdout=show_stdout)
+
+
+def extract_zip(file: Path, destination: Path):
+ """
+ Unpacks the zip file into destination preserving all permissions
+
+ TODO: Try to use zipfile module. Currently we cannot use zipfile module here because
+ extractAll() does not preserve permissions.
+
+ In case `unzip` is not available, the user is requested to install it manually
+ """
+ unzip = shutil.which("unzip")
+ if not unzip:
+ raise RuntimeError("Unable to find program unzip. Use `sudo apt-get install unzip`"
+ "to install it")
+
+ command = [unzip, str(file), "-d", str(destination)]
+ run_command(command=command, show_stdout=True)
+
+
+def extract_dmg(file: Path, destination: Path):
+ output = run_command(['hdiutil', 'attach', '-nobrowse', '-readonly', str(file)],
+ show_stdout=True, capture_stdout=True)
+
+ # find the mounted volume
+ result = re.search(r'/Volumes/(.*)', output)
+ if not result:
+ raise RuntimeError(f"Unable to find mounted volume for file {file}")
+ mounted_vol_name = result.group(1)
+ if not mounted_vol_name:
+ raise RuntimeError(f"Unable to find mounted volume for file {file}")
+
+ # copy files
+ shutil.copytree(f'/Volumes/{mounted_vol_name}/', destination, dirs_exist_ok=True)
+
+ # Detach mounted volume
+ run_command(['hdiutil', 'detach', f'/Volumes/{mounted_vol_name}'])
+
+
+def _download(url: str, destination: Path):
+ """
+ Download url to destination
+ """
+ headers, download_path = None, None
+ # https://github.com/tqdm/tqdm#hooks-and-callbacks
+ with DownloadProgressBar(unit='B', unit_scale=True, miniters=1, desc=url.split('/')[-1]) as t:
+ download_path, headers = request.urlretrieve(url=url, filename=destination,
+ reporthook=t.update_to)
+ assert Path(download_path).resolve() == destination
+
+
+def download_android_ndk(ndk_path: Path):
+ """
+ Downloads the given ndk_version into ndk_path
+ """
+ ndk_path = ndk_path / "android-ndk"
+ ndk_extension = "dmg" if sys.platform == "darwin" else "zip"
+ ndk_zip_path = ndk_path / f"android-ndk-r{ANDROID_NDK_VERSION}-{sys.platform}.{ndk_extension}"
+ ndk_version_path = ""
+ if sys.platform == "linux":
+ ndk_version_path = ndk_path / f"android-ndk-r{ANDROID_NDK_VERSION}"
+ elif sys.platform == "darwin":
+ ndk_version_path = (ndk_path
+ / f"AndroidNDK{ANDROID_NDK_VERSION_NUMBER_SUFFIX}.app/Contents/NDK")
+ else:
+ raise RuntimeError(f"Unsupported platform {sys.platform}")
+
+ if ndk_version_path.exists():
+ print(f"NDK path found in {str(ndk_version_path)}")
+ else:
+ try:
+ ndk_path.mkdir(parents=True, exist_ok=True)
+ url = (f"https://dl.google.com/android/repository"
+ f"/android-ndk-r{ANDROID_NDK_VERSION}-{sys.platform}.{ndk_extension}")
+
+ print(f"Downloading Android Ndk version r{ANDROID_NDK_VERSION}")
+ _download(url=url, destination=ndk_zip_path)
+
+ print("Unpacking Android Ndk")
+ if sys.platform == "darwin":
+ extract_dmg(file=(ndk_path
+ / f"android-ndk-r{ANDROID_NDK_VERSION}-{sys.platform}.{ndk_extension}"),
+ destination=ndk_path)
+ ndk_version_path = (ndk_version_path
+ / (f"AndroidNDK{ANDROID_NDK_VERSION_NUMBER_SUFFIX}.app"
+ "/Contents/NDK"))
+ else:
+ extract_zip(file=(ndk_path
+ / f"android-ndk-r{ANDROID_NDK_VERSION}-{sys.platform}.{ndk_extension}"),
+ destination=ndk_path)
+ except Exception as e:
+ print(f"Error occurred while downloading and unpacking Android NDK: {e}")
+ if ndk_path.exists():
+ shutil.rmtree(ndk_path)
+ sys.exit(1)
+
+ return ndk_version_path
+
+
+def download_android_commandlinetools(android_sdk_dir: Path):
+ """
+ Downloads Android commandline tools into cltools_path.
+ """
+ sdk_platform = sys.platform if sys.platform != "darwin" else "mac"
+ android_sdk_dir = android_sdk_dir / "android-sdk"
+ url = ("https://dl.google.com/android/repository/"
+ f"commandlinetools-{sdk_platform}-{DEFAULT_SDK_TAG}_latest.zip")
+ cltools_zip_path = (android_sdk_dir
+ / f"commandlinetools-{sdk_platform}-{DEFAULT_SDK_TAG}_latest.zip")
+ cltools_path = android_sdk_dir / "tools"
+
+ if cltools_path.exists():
+ print(f"Command-line tools found in {str(cltools_path)}")
+ else:
+ try:
+ android_sdk_dir.mkdir(parents=True, exist_ok=True)
+
+ print("Download Android Command Line Tools: "
+ f"commandlinetools-{sys.platform}-{DEFAULT_SDK_TAG}_latest.zip")
+ _download(url=url, destination=cltools_zip_path)
+
+ print("Unpacking Android Command Line Tools")
+ extract_zip(file=cltools_zip_path, destination=android_sdk_dir)
+ except Exception as e:
+ print(f"Error occurred while downloading and unpacking Android Command Line Tools: {e}")
+ if android_sdk_dir.exists():
+ shutil.rmtree(android_sdk_dir)
+ sys.exit(1)
+
+ return android_sdk_dir
+
+
+def android_list_build_tools_versions(sdk_manager: SdkManager):
+ """
+ List all the build-tools versions available for download
+ """
+ available_packages = sdk_manager.list_packages()
+ build_tools_versions = []
+ lines = available_packages.split('\n')
+
+ for line in lines:
+ if not line.strip().startswith('build-tools;'):
+ continue
+ package_name = line.strip().split(' ')[0]
+ if package_name.count(';') != 1:
+ raise RuntimeError(f"Unable to parse build-tools version: {package_name}")
+ ver = package_name.split(';')[1]
+
+ build_tools_versions.append(version.Version(ver))
+
+ return build_tools_versions
+
+
+def find_installed_buildtools_version(build_tools_dir: Path):
+ """
+ It is possible that the user has multiple build-tools installed. The newer version is generally
+ used. This function find the newest among the installed build-tools
+ """
+ versions = [version.Version(bt_dir.name) for bt_dir in build_tools_dir.iterdir()
+ if bt_dir.is_dir()]
+ return max(versions)
+
+
+def find_latest_buildtools_version(sdk_manager: SdkManager):
+ """
+ Uses sdk manager to find the latest build-tools version
+ """
+ available_build_tools_v = android_list_build_tools_versions(sdk_manager=sdk_manager)
+
+ if not available_build_tools_v:
+ raise RuntimeError('Unable to find any build tools available for download')
+
+ # find the latest build tools version that is not a release candidate
+ # release candidates end has rc in the version number
+ available_build_tools_v = [v for v in available_build_tools_v if "rc" not in str(v)]
+
+ return max(available_build_tools_v)
+
+
+def install_android_packages(android_sdk_dir: Path, android_api: str, dry_run: bool = False,
+ accept_license: bool = False, skip_update: bool = False):
+ """
+ Use the sdk manager to install build-tools, platform-tools and platform API
+ """
+ tools_dir = android_sdk_dir / "tools"
+ if not tools_dir.exists():
+ raise RuntimeError("Unable to find Android command-line tools in "
+ f"{str(tools_dir)}")
+
+ # incase of --verbose flag
+ show_output = (logging.getLogger().getEffectiveLevel() == logging.INFO)
+
+ sdk_manager = SdkManager(android_sdk_dir=android_sdk_dir, dry_run=dry_run)
+
+ # install/upgrade platform-tools
+ if not (android_sdk_dir / "platform-tools").exists():
+ print("Installing/Updating Android platform-tools")
+ sdk_manager.install("platform-tools", accept_license=accept_license,
+ show_stdout=show_output)
+ # The --update command is only relevant for platform tools
+ if not skip_update:
+ sdk_manager.install("--update", show_stdout=show_output)
+
+ # install/upgrade build-tools
+ buildtools_dir = android_sdk_dir / "build-tools"
+
+ if not buildtools_dir.exists():
+ latest_build_tools_v = find_latest_buildtools_version(sdk_manager=sdk_manager)
+ print(f"Installing Android build-tools version {latest_build_tools_v}")
+ sdk_manager.install(f"build-tools;{latest_build_tools_v}", show_stdout=show_output)
+ else:
+ if not skip_update:
+ latest_build_tools_v = find_latest_buildtools_version(sdk_manager=sdk_manager)
+ installed_build_tools_v = find_installed_buildtools_version(buildtools_dir)
+ if latest_build_tools_v > installed_build_tools_v:
+ print(f"Updating Android build-tools version to {latest_build_tools_v}")
+ sdk_manager.install(f"build-tools;{latest_build_tools_v}", show_stdout=show_output)
+ installed_build_tools_v = latest_build_tools_v
+
+ # install the platform API
+ platform_api_dir = android_sdk_dir / "platforms" / f"android-{android_api}"
+ if not platform_api_dir.exists():
+ print(f"Installing Android platform API {android_api}")
+ sdk_manager.install(f"platforms;android-{android_api}", show_stdout=show_output)
+
+ print("Android packages installation done")
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import sys
import logging
import xml.etree.ElementTree as ET
import zipfile
from pathlib import Path
-from typing import List
from . import AndroidConfig
from .. import BaseConfig, run_command
self.update_config()
- def __find_permissions(self, dependency_files: List[zipfile.Path]):
+ def __find_permissions(self, dependency_files: list[zipfile.Path]):
permissions = set()
for dependency_file in dependency_files:
xml_content = dependency_file.read_text()
permissions.add(permission.attrib['name'])
return permissions
- def __find_jars(self, dependency_files: List[zipfile.Path], jars_dir: Path):
+ def __find_jars(self, dependency_files: list[zipfile.Path], jars_dir: Path):
jars, init_classes = set(), set()
for dependency_file in dependency_files:
xml_content = dependency_file.read_text()
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import shutil
import zipfile
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import shutil
import zipfile
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import json
import subprocess
import sys
from pathlib import Path
-from typing import List
+from functools import lru_cache
+from . import DEFAULT_IGNORE_DIRS
+
"""
All utility functions for deployment
return command_str, output
-def run_qmlimportscanner(qml_files: List[Path], dry_run: bool):
+@lru_cache
+def run_qmlimportscanner(project_dir: Path, dry_run: bool):
"""
- Runs pyside6-qmlimportscanner to find all the imported qml modules
+ Runs pyside6-qmlimportscanner to find all the imported qml modules in project_dir
"""
- if not qml_files:
- return []
-
qml_modules = []
- cmd = ["pyside6-qmlimportscanner", "-qmlFiles"]
- cmd.extend([str(qml_file) for qml_file in qml_files])
+ cmd = ["pyside6-qmlimportscanner", "-rootPath", str(project_dir)]
+
+ for ignore_dir in DEFAULT_IGNORE_DIRS:
+ cmd.extend(["-exclude", ignore_dir])
if dry_run:
run_command(command=cmd, dry_run=True)
- # we need to run qmlimportscanner during dry_run as well to complete the
- # command being run by nuitka
+ # Run qmlimportscanner during dry_run as well to complete the command being run by nuitka
_, json_string = run_command(command=cmd, dry_run=False, fetch_output=True)
json_string = json_string.decode("utf-8")
json_array = json.loads(json_string)
qml_modules = [item['name'] for item in json_array if item['type'] == "module"]
+
return qml_modules
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import sys
import configparser
import logging
+import tempfile
import warnings
from configparser import ConfigParser
-from typing import List
from pathlib import Path
+from enum import Enum
-from project import ProjectData
-from . import (DEFAULT_APP_ICON, find_pyside_modules, find_permission_categories,
- QtDependencyReader, run_qmlimportscanner)
+from project_lib import ProjectData, DesignStudioProject
+from . import (DEFAULT_APP_ICON, DEFAULT_IGNORE_DIRS, find_pyside_modules,
+ find_permission_categories, QtDependencyReader, run_qmlimportscanner)
# Some QML plugins like QtCore are excluded from this list as they don't contribute much to
# executable size. Excluding them saves the extra processing of checking for them in files
class BaseConfig:
"""Wrapper class around any .spec file with function to read and set values for the .spec file
"""
+
def __init__(self, config_file: Path, comment_prefixes: str = "/",
existing_config_file: bool = False) -> None:
self.config_file = config_file
self.parser.read(self.config_file)
def update_config(self):
- logging.info(f"[DEPLOY] Creating {self.config_file}")
- with open(self.config_file, "w+") as config_file:
- self.parser.write(config_file, space_around_delimiters=True)
-
- def set_value(self, section: str, key: str, new_value: str, raise_warning: bool = True):
+ logging.info(f"[DEPLOY] Updating config file {self.config_file}")
+
+ # This section of code is done to preserve the formatting of the original deploy.spec
+ # file where there is blank line before the comments
+ with tempfile.NamedTemporaryFile('w+', delete=False) as temp_file:
+ self.parser.write(temp_file, space_around_delimiters=True)
+ temp_file_path = temp_file.name
+
+ # Read the temporary file and write back to the original file with blank lines before
+ # comments
+ with open(temp_file_path, 'r') as temp_file, open(self.config_file, 'w') as config_file:
+ previous_line = None
+ for line in temp_file:
+ if (line.lstrip().startswith('#') and previous_line is not None
+ and not previous_line.lstrip().startswith('#')):
+ config_file.write('\n')
+ config_file.write(line)
+ previous_line = line
+
+ # Clean up the temporary file
+ Path(temp_file_path).unlink()
+
+ def set_value(self, section: str, key: str, new_value: str, raise_warning: bool = True) -> None:
try:
current_value = self.get_value(section, key, ignore_fail=True)
if current_value != new_value:
self.parser.set(section, key, new_value)
except configparser.NoOptionError:
- if raise_warning:
- logging.warning(f"[DEPLOY] Key {key} does not exist")
+ if not raise_warning:
+ return
+ logging.warning(f"[DEPLOY] Set key '{key}': Key does not exist in section '{section}'")
except configparser.NoSectionError:
- if raise_warning:
- logging.warning(f"[DEPLOY] Section {section} does not exist")
+ if not raise_warning:
+ return
+ logging.warning(f"[DEPLOY] Section '{section}' does not exist")
- def get_value(self, section: str, key: str, ignore_fail: bool = False):
+ def get_value(self, section: str, key: str, ignore_fail: bool = False) -> str | None:
try:
return self.parser.get(section, key)
except configparser.NoOptionError:
- if not ignore_fail:
- logging.warning(f"[DEPLOY] Key {key} does not exist")
+ if ignore_fail:
+ return None
+ logging.warning(f"[DEPLOY] Get key '{key}': Key does not exist in section {section}")
except configparser.NoSectionError:
- if not ignore_fail:
- logging.warning(f"[DEPLOY] Section {section} does not exist")
+ if ignore_fail:
+ return None
+ logging.warning(f"[DEPLOY] Section '{section}': does not exist")
class Config(BaseConfig):
"""
def __init__(self, config_file: Path, source_file: Path, python_exe: Path, dry_run: bool,
- existing_config_file: bool = False, extra_ignore_dirs: List[str] = None):
+ existing_config_file: bool = False, extra_ignore_dirs: list[str] = None,
+ name: str = None):
super().__init__(config_file=config_file, existing_config_file=existing_config_file)
self.extra_ignore_dirs = extra_ignore_dirs
)
)
- self.title = self.get_value("app", "title")
+ # set application name
+ self.title = self.set_or_fetch(config_property_val=name, config_property_key="title")
# set application icon
config_icon = self.get_value("app", "icon")
if config_icon:
- self.icon = str(Path(config_icon).resolve())
+ self._icon = str(Path(config_icon).resolve())
else:
self.icon = DEFAULT_APP_ICON
- self.project_dir = None
- if self.get_value("app", "project_dir"):
- self.project_dir = Path(self.get_value("app", "project_dir")).absolute()
- else:
- self._find_and_set_project_dir()
-
- self.exe_dir = None
- if self.get_value("app", "exec_directory"):
- self.exe_dir = Path(self.get_value("app", "exec_directory")).absolute()
+ proj_dir = self.get_value("app", "project_dir")
+ if proj_dir:
+ self._project_dir = Path(proj_dir).resolve()
else:
- self._find_and_set_exe_dir()
+ self.project_dir = self._find_project_dir()
- self.project_data: ProjectData = None
- if self.get_value("app", "project_file"):
- project_file = Path(self.get_value("app", "project_file")).absolute()
- self.project_data = ProjectData(project_file=project_file)
+ exe_directory = self.get_value("app", "exec_directory")
+ if exe_directory:
+ self._exe_dir = Path(exe_directory).absolute()
else:
- self._find_and_set_project_file()
+ self.exe_dir = self._find_exe_dir()
- self.qml_files = []
- config_qml_files = self.get_value("qt", "qml_files")
- if config_qml_files and self.project_dir and self.existing_config_file:
- self.qml_files = [Path(self.project_dir) / file for file in config_qml_files.split(",")]
+ self._project_file = None
+ proj_file = self.get_value("app", "project_file")
+ if proj_file:
+ self._project_file = self.project_dir / proj_file
else:
- self._find_and_set_qml_files()
+ proj_file = self._find_project_file()
+ if proj_file:
+ self.project_file = proj_file
+
+ self.project_data = None
+ if self.project_file and self.project_file.exists():
+ self.project_data = ProjectData(project_file=self.project_file)
+
+ self._qml_files = []
+ # Design Studio projects include the qml files using Qt resources
+ if source_file and not DesignStudioProject.is_ds_project(source_file):
+ config_qml_files = self.get_value("qt", "qml_files")
+ if config_qml_files and self.project_dir and self.existing_config_file:
+ self._qml_files = [Path(self.project_dir)
+ / file for file in config_qml_files.split(",")]
+ else:
+ self.qml_files = self._find_qml_files()
- self.excluded_qml_plugins = []
- if self.get_value("qt", "excluded_qml_plugins") and self.existing_config_file:
- self.excluded_qml_plugins = self.get_value("qt", "excluded_qml_plugins").split(",")
+ self._excluded_qml_plugins = []
+ excl_qml_plugins = self.get_value("qt", "excluded_qml_plugins")
+ if excl_qml_plugins and self.existing_config_file:
+ self._excluded_qml_plugins = excl_qml_plugins.split(",")
else:
- self._find_and_set_excluded_qml_plugins()
+ self.excluded_qml_plugins = self._find_excluded_qml_plugins()
- self._generated_files_path = self.project_dir / "deployment"
+ self._generated_files_path = self.source_file.parent / "deployment"
self.modules = []
def set_or_fetch(self, config_property_val, config_property_key, config_property_group="app"):
"""
- Write to config_file if 'config_property_key' is known without config_file
- Fetch and return from config_file if 'config_property_key' is unknown, but
- config_file exists
- Otherwise, raise an exception
+ Set the configuration value if provided, otherwise fetch the existing value.
+ Raise an exception if neither is available.
+
+ :param value: The value to set if provided.
+ :param key: The configuration key.
+ :param group: The configuration group (default is "app").
+ :return: The configuration value.
+ :raises RuntimeError: If no value is provided and no existing value is found.
"""
+ existing_value = self.get_value(config_property_group, config_property_key)
+
if config_property_val:
self.set_value(config_property_group, config_property_key, str(config_property_val))
return config_property_val
- elif self.get_value(config_property_group, config_property_key):
- return self.get_value(config_property_group, config_property_key)
+ elif existing_value:
+ return existing_value
else:
raise RuntimeError(
- f"[DEPLOY] No {config_property_key} specified in config file or as cli option"
+ f"[DEPLOY] No value for {config_property_key} specified in config file or as cli"
+ " option"
)
@property
@qml_files.setter
def qml_files(self, qml_files):
self._qml_files = qml_files
+ qml_files = [str(file.absolute().relative_to(self.project_dir.absolute()))
+ if file.absolute().is_relative_to(self.project_dir) else str(file.absolute())
+ for file in self.qml_files]
+ self.set_value("qt", "qml_files", ",".join(qml_files))
@property
def project_dir(self):
@project_dir.setter
def project_dir(self, project_dir):
self._project_dir = project_dir
+ self.set_value("app", "project_dir", str(project_dir))
+
+ @property
+ def project_file(self):
+ return self._project_file
+
+ @project_file.setter
+ def project_file(self, project_file):
+ self._project_file = project_file
+ self.set_value("app", "project_file", str(project_file.relative_to(self.project_dir)))
@property
def title(self):
@title.setter
def title(self, title):
self._title = title
- self.set_value("app", "title", title)
@property
def icon(self):
@source_file.setter
def source_file(self, source_file: Path):
self._source_file = source_file
+ self.set_value("app", "input_file", str(source_file))
@property
def python_path(self):
@excluded_qml_plugins.setter
def excluded_qml_plugins(self, excluded_qml_plugins):
self._excluded_qml_plugins = excluded_qml_plugins
+ if excluded_qml_plugins: # check required for Android
+ self.set_value("qt", "excluded_qml_plugins", ",".join(excluded_qml_plugins))
@property
def exe_dir(self):
@exe_dir.setter
def exe_dir(self, exe_dir: Path):
self._exe_dir = exe_dir
+ self.set_value("app", "exec_directory", str(exe_dir))
@property
def modules(self):
self._modules = modules
self.set_value("qt", "modules", ",".join(modules))
- def _find_and_set_qml_files(self):
- """Fetches all the qml_files in the folder and sets them if the
- field qml_files is empty in the config_dir"""
+ def _find_qml_files(self):
+ """
+ Fetches all the qml_files in the folder and sets them if the
+ field qml_files is empty in the config_file
+ """
+ qml_files = []
if self.project_data:
- qml_files = self.project_data.qml_files
+ qml_files = [(self.project_dir / str(qml_file)) for qml_file in
+ self.project_data.qml_files]
for sub_project_file in self.project_data.sub_projects_files:
- qml_files.extend(ProjectData(project_file=sub_project_file).qml_files)
- self.qml_files = qml_files
+ qml_files.extend([self.project_dir / str(qml_file) for qml_file in
+ ProjectData(project_file=sub_project_file).qml_files])
else:
- qml_files_temp = None
- if self.source_file and self.python_path:
- if not self.qml_files:
- qml_files_temp = list(self.source_file.parent.glob("**/*.qml"))
-
- # add all QML files, excluding the ones shipped with installed PySide6
- # The QML files shipped with PySide6 gets added if venv is used,
- # because of recursive glob
- if self.python_path.parent.parent == self.source_file.parent:
- # python venv path is inside the main source dir
- qml_files_temp = list(
- set(qml_files_temp) - set(self.python_path.parent.parent.rglob("*.qml"))
- )
-
- if len(qml_files_temp) > 500:
- if "site-packages" in str(qml_files_temp[-1]):
- raise RuntimeError(
- "You are including a lot of QML files from a local virtual env."
- " This can lead to errors in deployment."
- )
- else:
- warnings.warn(
- "You seem to include a lot of QML files. This can lead to errors in "
- "deployment."
- )
-
- if qml_files_temp:
- extra_qml_files = [Path(file) for file in qml_files_temp]
- self.qml_files.extend(extra_qml_files)
- if self.qml_files:
- self.set_value(
- "qt",
- "qml_files",
- ",".join([str(file.absolute().relative_to(self.project_dir))
- for file in self.qml_files]),
- )
- logging.info("[DEPLOY] QML files identified and set in config_file")
+ # Filter out files from DEFAULT_IGNORE_DIRS
+ qml_files = [
+ file for file in self.project_dir.glob("**/*.qml")
+ if all(part not in file.parts for part in DEFAULT_IGNORE_DIRS)
+ ]
+
+ if len(qml_files) > 500:
+ warnings.warn(
+ "You seem to include a lot of QML files from "
+ f"{self.project_dir}. This can lead to errors in deployment."
+ )
+
+ return qml_files
+
+ def _find_project_dir(self) -> Path:
+ if DesignStudioProject.is_ds_project(self.source_file):
+ return DesignStudioProject(self.source_file).project_dir
- def _find_and_set_project_dir(self):
# there is no other way to find the project_dir than assume it is the parent directory
# of source_file
- self.project_dir = self.source_file.parent
+ return self.source_file.parent
- # update input_file path
- self.set_value("app", "input_file", str(self.source_file.relative_to(self.project_dir)))
+ def _find_project_file(self) -> Path | None:
+ if not self.source_file:
+ raise RuntimeError("[DEPLOY] Source file not set in config file")
- if self.project_dir != Path.cwd():
- self.set_value("app", "project_dir", str(self.project_dir))
+ if DesignStudioProject.is_ds_project(self.source_file):
+ pyproject_location = self.source_file.parent
else:
- self.set_value("app", "project_dir", str(self.project_dir.relative_to(Path.cwd())))
-
- def _find_and_set_project_file(self):
- if self.project_dir:
- files = list(self.project_dir.glob("*.pyproject"))
- else:
- logging.exception("[DEPLOY] Project directory not set in config file")
- raise
+ pyproject_location = self.project_dir
+ files = list(pyproject_location.glob("*.pyproject"))
if not files:
logging.info("[DEPLOY] No .pyproject file found. Project file not set")
- elif len(files) > 1:
- logging.warning("DEPLOY: More that one .pyproject files found. Project file not set")
- raise
- else:
- self.project_data = ProjectData(files[0])
- self.set_value("app", "project_file", str(files[0].relative_to(self.project_dir)))
- logging.info(f"[DEPLOY] Project file {files[0]} found and set in config file")
+ return None
+ if len(files) > 1:
+ warnings.warn("DEPLOY: More that one .pyproject files found. Project file not set")
+ return None
- def _find_and_set_excluded_qml_plugins(self):
- if self.qml_files:
- self.qml_modules = set(run_qmlimportscanner(qml_files=self.qml_files,
- dry_run=self.dry_run))
- self.excluded_qml_plugins = EXCLUDED_QML_PLUGINS.difference(self.qml_modules)
+ return files[0]
- # needed for dry_run testing
- self.excluded_qml_plugins = sorted(self.excluded_qml_plugins)
+ def _find_excluded_qml_plugins(self) -> list[str] | None:
+ if not self.qml_files and not DesignStudioProject.is_ds_project(self.source_file):
+ return None
- if self.excluded_qml_plugins:
- self.set_value("qt", "excluded_qml_plugins", ",".join(self.excluded_qml_plugins))
+ self.qml_modules = set(run_qmlimportscanner(project_dir=self.project_dir,
+ dry_run=self.dry_run))
+ excluded_qml_plugins = EXCLUDED_QML_PLUGINS.difference(self.qml_modules)
- def _find_and_set_exe_dir(self):
+ # sorting needed for dry_run testing
+ return sorted(excluded_qml_plugins)
+
+ def _find_exe_dir(self) -> Path:
if self.project_dir == Path.cwd():
- self.exe_dir = self.project_dir.relative_to(Path.cwd())
- else:
- self.exe_dir = self.project_dir
- self.exe_dir = Path(
- self.set_or_fetch(
- config_property_val=self.exe_dir, config_property_key="exec_directory"
- )
- ).absolute()
+ return self.project_dir.relative_to(Path.cwd())
+
+ return self.project_dir
- def _find_and_set_pysidemodules(self):
- self.modules = find_pyside_modules(project_dir=self.project_dir,
- extra_ignore_dirs=self.extra_ignore_dirs,
- project_data=self.project_data)
+ def _find_pysidemodules(self) -> list[str]:
+ modules = find_pyside_modules(project_dir=self.project_dir,
+ extra_ignore_dirs=self.extra_ignore_dirs,
+ project_data=self.project_data)
logging.info("The following PySide modules were found from the Python files of "
- f"the project {self.modules}")
+ f"the project {modules}")
+ return modules
- def _find_and_set_qtquick_modules(self):
+ def _find_qtquick_modules(self) -> list[str]:
"""Identify if QtQuick is used in QML files and add them as dependency
"""
extra_modules = []
- if not self.qml_modules:
- self.qml_modules = set(run_qmlimportscanner(qml_files=self.qml_files,
+ if not self.qml_modules and self.qml_files:
+ self.qml_modules = set(run_qmlimportscanner(project_dir=self.project_dir,
dry_run=self.dry_run))
if "QtQuick" in self.qml_modules:
if "QtQuick.Controls" in self.qml_modules:
extra_modules.append("QuickControls2")
- self.modules += extra_modules
+ return extra_modules
class DesktopConfig(Config):
"""Wrapper class around pysidedeploy.spec, but specific to Desktop deployment
"""
+
+ class NuitkaMode(Enum):
+ ONEFILE = "onefile"
+ STANDALONE = "standalone"
+
def __init__(self, config_file: Path, source_file: Path, python_exe: Path, dry_run: bool,
- existing_config_file: bool = False, extra_ignore_dirs: List[str] = None):
+ existing_config_file: bool = False, extra_ignore_dirs: list[str] = None,
+ mode: str = "onefile", name: str = None):
super().__init__(config_file, source_file, python_exe, dry_run, existing_config_file,
- extra_ignore_dirs)
+ extra_ignore_dirs, name=name)
self.dependency_reader = QtDependencyReader(dry_run=self.dry_run)
- if self.get_value("qt", "modules"):
- self.modules = self.get_value("qt", "modules").split(",")
+ modules = self.get_value("qt", "modules")
+ if modules:
+ self._modules = modules.split(",")
else:
- self._find_and_set_pysidemodules()
- self._find_and_set_qtquick_modules()
- self._find_dependent_qt_modules()
+ modules = self._find_pysidemodules()
+ modules += self._find_qtquick_modules()
+ modules += self._find_dependent_qt_modules(modules=modules)
+ # remove duplicates
+ self.modules = list(set(modules))
self._qt_plugins = []
if self.get_value("qt", "plugins"):
if nuitka_macos_permissions:
self._permissions = nuitka_macos_permissions.split(",")
else:
- self._find_and_set_permissions()
+ self.permissions = self._find_permissions()
+
+ self._mode = self.NuitkaMode.ONEFILE
+ if self.get_value("nuitka", "mode") == self.NuitkaMode.STANDALONE.value:
+ self._mode = self.NuitkaMode.STANDALONE
+ elif mode == self.NuitkaMode.STANDALONE.value:
+ self.mode = self.NuitkaMode.STANDALONE
+
+ if DesignStudioProject.is_ds_project(self.source_file):
+ ds_project = DesignStudioProject(self.source_file)
+ if not ds_project.compiled_resources_available():
+ raise RuntimeError(f"[DEPLOY] Compiled resources file not found: "
+ f"{ds_project.compiled_resources_file.absolute()}. "
+ f"Build the project using 'pyside6-project build' or compile "
+ f"the resources manually using pyside6-rcc")
@property
def qt_plugins(self):
self._permissions = permissions
self.set_value("nuitka", "macos.permissions", ",".join(permissions))
- def _find_dependent_qt_modules(self):
+ @property
+ def mode(self):
+ return self._mode
+
+ @mode.setter
+ def mode(self, mode: NuitkaMode):
+ self._mode = mode
+ self.set_value("nuitka", "mode", mode.value)
+
+ def _find_dependent_qt_modules(self, modules: list[str]) -> list[str]:
"""
Given pysidedeploy_config.modules, find all the other dependent Qt modules.
"""
- all_modules = set(self.modules)
+ all_modules = set(modules)
if not self.dependency_reader.lib_reader:
warnings.warn(f"[DEPLOY] Unable to find {self.dependency_reader.lib_reader_name}. This "
- "tool helps to find the Qt module dependencies of the application. "
- "Skipping checking for dependencies.", category=RuntimeWarning)
- return
+ f"tool helps to find the Qt module dependencies of the application. "
+ f"Skipping checking for dependencies.", category=RuntimeWarning)
+ return []
- for module_name in self.modules:
+ for module_name in modules:
self.dependency_reader.find_dependencies(module=module_name, used_modules=all_modules)
- self.modules = list(all_modules)
+ return list(all_modules)
- def _find_and_set_permissions(self):
+ def _find_permissions(self) -> list[str]:
"""
Finds and sets the usage description string required for each permission requested by the
macOS application.
if perm_category in PERMISSION_MAP:
permissions.append(PERMISSION_MAP[perm_category])
- self.permissions = permissions
+ return permissions
# Source file path
input_file =
-# Directory where exec is stored
+# Directory where the executable output is generated
exec_directory =
# Path to .pyproject project file
python_path =
# python packages to install
-# ordered-set: increase compile time performance of nuitka packaging
-# zstandard: provides final executable size optimization
-packages = Nuitka==2.3.2
+packages = Nuitka==2.5.1
# buildozer: for deploying Android application
android_packages = buildozer==1.5.0,cython==0.29.33
# Qt modules used. Comma separated
modules =
-# Qt plugins used by the application
+# Qt plugins used by the application. Only relevant for desktop deployment. For Qt plugins used
+# in Android application see [android][plugins]
plugins =
[android]
# eg: NSCameraUsageDescription:CameraAccess
macos.permissions =
+# mode of using Nuitka. Accepts standalone or onefile. Default is onefile.
+mode = onefile
+
# (str) specify any extra nuitka arguments
# eg: extra_args = --show-modules --follow-stdlib
extra_args = --quiet --noinclude-qt-translations
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import ast
import re
import shutil
import sys
from pathlib import Path
-from typing import List, Set
from functools import lru_cache
-from . import IMPORT_WARNING_PYSIDE, run_command
+from . import IMPORT_WARNING_PYSIDE, DEFAULT_IGNORE_DIRS, run_command
@lru_cache(maxsize=None)
-def get_py_files(project_dir: Path, extra_ignore_dirs: List[Path] = None, project_data=None):
+def get_py_files(project_dir: Path, extra_ignore_dirs: tuple[Path] = None, project_data=None):
"""Finds and returns all the Python files in the project
"""
py_candidates = []
- ignore_dirs = ["__pycache__", "env", "venv", "deployment"]
+ ignore_dirs = DEFAULT_IGNORE_DIRS.copy()
if project_data:
py_candidates = project_data.python_files
# incase there is not .pyproject file, search all python files in project_dir, except
# ignore_dirs
if extra_ignore_dirs:
- ignore_dirs.extend(extra_ignore_dirs)
+ ignore_dirs.update(extra_ignore_dirs)
# find relevant .py files
_walk = os.walk(project_dir)
return tree
-def find_permission_categories(project_dir: Path, extra_ignore_dirs: List[Path] = None,
+def find_permission_categories(project_dir: Path, extra_ignore_dirs: list[Path] = None,
project_data=None):
"""Given the project directory, finds all the permission categories required by the
project. eg: Camera, Bluetooth, Contacts etc.
return set(perm_categories)
+ if extra_ignore_dirs is not None:
+ extra_ignore_dirs = tuple(extra_ignore_dirs)
py_candidates = get_py_files(project_dir, extra_ignore_dirs, project_data)
for py_candidate in py_candidates:
all_perm_categories = all_perm_categories.union(pyside_permission_imports(py_candidate))
return all_perm_categories
-def find_pyside_modules(project_dir: Path, extra_ignore_dirs: List[Path] = None,
+def find_pyside_modules(project_dir: Path, extra_ignore_dirs: list[Path] = None,
project_data=None):
"""
Searches all the python files in the project to find all the PySide modules used by
all_modules = set()
mod_pattern = re.compile("PySide6.Qt(?P<mod_name>.*)")
+ @lru_cache
def pyside_module_imports(py_file: Path):
modules = []
try:
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom):
main_mod_name = node.module
- if main_mod_name.startswith("PySide6"):
+ if main_mod_name and main_mod_name.startswith("PySide6"):
if main_mod_name == "PySide6":
# considers 'from PySide6 import QtCore'
for imported_module in node.names:
return set(modules)
+ if extra_ignore_dirs is not None:
+ extra_ignore_dirs = tuple(extra_ignore_dirs)
py_candidates = get_py_files(project_dir, extra_ignore_dirs, project_data)
for py_candidate in py_candidates:
all_modules = all_modules.union(pyside_module_imports(py_candidate))
"""
Finds the path to the Qt libs directory inside PySide6 package installation
"""
+ # PYSIDE-2785 consider dist-packages for Debian based systems
for possible_site_package in site.getsitepackages():
- if possible_site_package.endswith("site-packages"):
+ if possible_site_package.endswith(("site-packages", "dist-packages")):
self.pyside_install_dir = Path(possible_site_package) / "PySide6"
+ if self.pyside_install_dir.exists():
+ break
if not self.pyside_install_dir:
- print("Unable to find site-packages. Exiting ...")
+ print("Unable to find where PySide6 is installed. Exiting ...")
sys.exit(-1)
if sys.platform == "win32":
def lib_reader(self):
return self._lib_reader
- def find_dependencies(self, module: str, used_modules: Set[str] = None):
+ def find_dependencies(self, module: str, used_modules: set[str] = None):
"""
Given a Qt module, find all the other Qt modules it is dependent on and add it to the
'used_modules' set
else:
logging.info(f"[DEPLOY] No Qt dependencies found for {module}")
- def find_plugin_dependencies(self, used_modules: List[str], python_exe: Path) -> List[str]:
+ def find_plugin_dependencies(self, used_modules: list[str], python_exe: Path) -> list[str]:
"""
Given the modules used by the application, returns all the required plugins
"""
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import logging
import shutil
from pathlib import Path
from . import EXE_FORMAT
-from .config import Config
+from .config import Config, DesktopConfig
def config_option_exists():
def cleanup(config: Config, is_android: bool = False):
"""
- Cleanup the generated build folders/files
+ Cleanup the generated build folders/files.
+
+ Parameters:
+ config (Config): The configuration object containing paths and settings.
+ is_android (bool): Flag indicating if the cleanup is for an Android project. Default is False.
"""
if config.generated_files_path.exists():
- shutil.rmtree(config.generated_files_path)
- logging.info("[DEPLOY] Deployment directory purged")
+ try:
+ shutil.rmtree(config.generated_files_path)
+ logging.info("[DEPLOY] Deployment directory purged")
+ except PermissionError as e:
+ print(f"{type(e).__name__}: {e}")
+ logging.warning(f"[DEPLOY] Could not delete {config.generated_files_path}")
if is_android:
buildozer_spec: Path = config.project_dir / "buildozer.spec"
if buildozer_spec.exists():
- buildozer_spec.unlink()
- logging.info(f"[DEPLOY] {str(buildozer_spec)} removed")
+ try:
+ buildozer_spec.unlink()
+ logging.info(f"[DEPLOY] {str(buildozer_spec)} removed")
+ except PermissionError as e:
+ print(f"{type(e).__name__}: {e}")
+ logging.warning(f"[DEPLOY] Could not delete {buildozer_spec}")
buildozer_build: Path = config.project_dir / ".buildozer"
if buildozer_build.exists():
- shutil.rmtree(buildozer_build)
- logging.info(f"[DEPLOY] {str(buildozer_build)} removed")
+ try:
+ shutil.rmtree(buildozer_build)
+ logging.info(f"[DEPLOY] {str(buildozer_build)} removed")
+ except PermissionError as e:
+ print(f"{type(e).__name__}: {e}")
+ logging.warning(f"[DEPLOY] Could not delete {buildozer_build}")
-def create_config_file(dry_run: bool = False, config_file: Path = None, main_file: Path = None):
+def create_config_file(main_file: Path, dry_run: bool = False):
"""
- Sets up a new pysidedeploy.spec or use an existing config file
+ Creates a new pysidedeploy.spec
"""
- if main_file:
- if main_file.parent != Path.cwd():
- config_file = main_file.parent / "pysidedeploy.spec"
- else:
- config_file = Path.cwd() / "pysidedeploy.spec"
-
+ config_file = main_file.parent / "pysidedeploy.spec"
logging.info(f"[DEPLOY] Creating config file {config_file}")
- if not dry_run:
- shutil.copy(Path(__file__).parent / "default.spec", config_file)
+ default_config_file = Path(__file__).parent / "default.spec"
# the config parser needs a reference to parse. So, in the case of --dry-run
# use the default.spec file.
if dry_run:
- config_file = Path(__file__).parent / "default.spec"
+ return default_config_file
+ shutil.copy(default_config_file, config_file)
return config_file
-def finalize(config: Config):
+def finalize(config: DesktopConfig):
"""
Copy the executable into the final location
For Android deployment, this is done through buildozer
"""
- generated_exec_path = config.generated_files_path / (config.source_file.stem + EXE_FORMAT)
- if generated_exec_path.exists() and config.exe_dir:
- if sys.platform == "darwin":
- shutil.copytree(generated_exec_path, config.exe_dir / (config.title + EXE_FORMAT),
- dirs_exist_ok=True)
- else:
- shutil.copy(generated_exec_path, config.exe_dir)
- print("[DEPLOY] Executed file created in "
- f"{str(config.exe_dir / (config.source_file.stem + EXE_FORMAT))}")
+ exe_format = EXE_FORMAT
+ if config.mode == DesktopConfig.NuitkaMode.STANDALONE and sys.platform != "darwin":
+ exe_format = ".dist"
+
+ generated_exec_path = config.generated_files_path / (config.source_file.stem + exe_format)
+ if not generated_exec_path.exists():
+ logging.error(f"[DEPLOY] Executable not found at {generated_exec_path.absolute()}")
+ return
+
+ logging.info(f"[DEPLOY] executable generated at {generated_exec_path.absolute()}")
+ if not config.exe_dir:
+ logging.info("[DEPLOY] Not copying output executable because no output directory specified")
+ return
+
+ output_path = config.exe_dir / (config.title + exe_format)
+
+ if sys.platform == "darwin" or config.mode == DesktopConfig.NuitkaMode.STANDALONE:
+ # Copy the folder that contains the executable
+ logging.info(f"[DEPLOY] copying generated folder to {output_path.absolute()}")
+ shutil.copytree(generated_exec_path, output_path, dirs_exist_ok=True)
+ else:
+ # Copy a single file
+ logging.info(f"[DEPLOY] copying generated file to {output_path.absolute()}")
+ shutil.copy(generated_exec_path, output_path)
+
+ print(f"[DEPLOY] Executed file created in {output_path.absolute()}")
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+# enables to use typehints for classes that has not been defined yet or imported
+# used for resolving circular imports
+from __future__ import annotations
import logging
import os
+import shlex
import sys
from pathlib import Path
-from typing import List
-from . import MAJOR_VERSION, run_command
+from project_lib import DesignStudioProject
+from . import MAJOR_VERSION, run_command, DEFAULT_IGNORE_DIRS, PLUGINS_TO_REMOVE
+from .config import DesktopConfig
class Nuitka:
"generic" # plugins that error with Nuitka
]
- # .webp are considered to be dlls by Nuitka instead of data files causing
- # the packaging to fail
- # https://github.com/Nuitka/Nuitka/issues/2854
- # TODO: Remove .webp when the issue is fixed
- self.files_to_ignore = [".cpp.o", ".qsb", ".webp"]
+ self.files_to_ignore = [".cpp.o", ".qsb"]
@staticmethod
def icon_option():
else:
return "--macos-app-icon"
- def create_executable(self, source_file: Path, extra_args: str, qml_files: List[Path],
- qt_plugins: List[str], excluded_qml_plugins: List[str], icon: str,
- dry_run: bool, permissions: List[str]):
+ def _create_windows_command(self, source_file: Path, command: list):
+ """
+ Special case for Windows where the command length is limited to 8191 characters.
+ """
+
+ # if the platform is windows and the command is more than 8191 characters, the command
+ # will fail with the error message "The command line is too long". To avoid this, we will
+ # we will move the source_file to the intermediate source file called deploy_main.py, and
+ # include the Nuitka options direcly in the main file as mentioned in
+ # https://nuitka.net/user-documentation/user-manual.html#nuitka-project-options
+
+ # convert command into a format recognized by Nuitka when written to the main file
+ # the first item is ignore because it is 'python -m nuitka'
+ nuitka_comment_options = []
+ for command_entry in command[4:]:
+ nuitka_comment_options.append(f"# nuitka-project: {command_entry}")
+ nuitka_comment_options_str = "\n".join(nuitka_comment_options)
+ nuitka_comment_options_str += "\n"
+
+ # read the content of the source file
+ new_source_content = (nuitka_comment_options_str
+ + Path(source_file).read_text(encoding="utf-8"))
+
+ # create and write back the new source content to deploy_main.py
+ new_source_file = source_file.parent / "deploy_main.py"
+ new_source_file.write_text(new_source_content, encoding="utf-8")
+
+ return new_source_file
+
+ def create_executable(self, source_file: Path, extra_args: str, qml_files: list[Path],
+ qt_plugins: list[str], excluded_qml_plugins: list[str], icon: str,
+ dry_run: bool, permissions: list[str],
+ mode: DesktopConfig.NuitkaMode) -> str:
qt_plugins = [plugin for plugin in qt_plugins if plugin not in self.qt_plugins_to_ignore]
- extra_args = extra_args.split()
+ extra_args = shlex.split(extra_args)
+ # macOS uses the --standalone option by default to create an app bundle
if sys.platform == "darwin":
# create an app bundle
extra_args.extend(["--standalone", "--macos-create-app-bundle"])
for permission in permissions:
extra_args.append(permission_pattern.format(permission=permission))
else:
- extra_args.append("--onefile")
+ extra_args.append(f"--{mode.value}")
qml_args = []
if qml_files:
+ # include all the subdirectories in the project directory as data directories
+ # This includes all the qml modules
+ all_relevant_subdirs = []
+ for subdir in source_file.parent.iterdir():
+ if subdir.is_dir() and subdir.name not in DEFAULT_IGNORE_DIRS:
+ extra_args.append(f"--include-data-dir={subdir}="
+ f"./{subdir.name}")
+ all_relevant_subdirs.append(subdir)
+
+ # find all the qml files that are not included via the data directories
+ extra_qml_files = [file for file in qml_files
+ if file.parent not in all_relevant_subdirs]
+
# This will generate options for each file using:
# --include-data-files=ABSOLUTE_PATH_TO_FILE=RELATIVE_PATH_TO ROOT
- # for each file. This will preserve the directory structure of QML resources.
+ # for each file.
qml_args.extend(
[f"--include-data-files={qml_file.resolve()}="
- f"./{qml_file.resolve().relative_to(source_file.parent)}"
- for qml_file in qml_files]
+ f"./{qml_file.resolve().relative_to(source_file.resolve().parent)}"
+ for qml_file in extra_qml_files]
)
+
+ if qml_files or DesignStudioProject.is_ds_project(source_file):
# add qml plugin. The `qml`` plugin name is not present in the module json files shipped
# with Qt and hence not in `qt_plugins``. However, Nuitka uses the 'qml' plugin name to
# include the necessary qml plugins. There we have to add it explicitly for a qml
if qt_plugins:
# sort qt_plugins so that the result is definitive when testing
qt_plugins.sort()
+ # remove the following plugins from the qt_plugins list as Nuitka only checks
+ # for plugins within PySide6/Qt/plugins folder, and the following plugins
+ # are not present in the PySide6/Qt/plugins folder
+ qt_plugins = [plugin for plugin in qt_plugins if plugin not in PLUGINS_TO_REMOVE]
qt_plugins_str = ",".join(qt_plugins)
command.append(f"--include-qt-plugins={qt_plugins_str}")
+ long_command = False
+ if sys.platform == "win32" and len(" ".join(str(cmd) for cmd in command)) > 7000:
+ logging.info("[DEPLOY] Nuitka command too long for Windows. "
+ "Copying the contents of main Python file to an intermediate "
+ "deploy_main.py file")
+ long_command = True
+ new_source_file = self._create_windows_command(source_file=source_file, command=command)
+ command = self.nuitka + [os.fspath(new_source_file)]
+
command_str, _ = run_command(command=command, dry_run=dry_run)
+
+ # if deploy_main.py exists, delete it after the command is run
+ if long_command:
+ os.remove(source_file.parent / "deploy_main.py")
+
return command_str
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import logging
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import ast
import json
import tokenize
from argparse import ArgumentParser, RawTextHelpFormatter
from pathlib import Path
-from typing import Dict, List, Optional, Set, Tuple, Union
+from typing import Union
DESCRIPTION = """Parses Python source code to create QObject metatype
QOBJECT_DERIVED = ["QObject", "QQuickItem", "QQuickPaintedItem"] + ITEM_MODELS
+# Python 3.9 does not support this syntax, yet
+# AstDecorator = ast.Name | ast.Call
+# AstPySideTypeSpec = ast.Name | ast.Constant
AstDecorator = Union[ast.Name, ast.Call]
AstPySideTypeSpec = Union[ast.Name, ast.Constant]
-ClassList = List[dict]
+ClassList = list[dict]
-PropertyEntry = Dict[str, Union[str, int, bool]]
+# PropertyEntry = dict[str, str | int | bool]
+PropertyEntry = dict[str, Union[str, int, bool]]
-Argument = Dict[str, str]
-Arguments = List[Argument]
-Signal = Dict[str, Union[str, Arguments]]
-Slot = Dict[str, Union[str, Arguments]]
+Argument = dict[str, str]
+Arguments = list[Argument]
+# Signal = dict[str, str | Arguments]
+# Slot = dict[str, str | Arguments]
+Signal = dict[str, Union[str, Arguments]]
+Slot = dict[str, Union[str, Arguments]]
-def _decorator(name: str, value: str) -> Dict[str, str]:
+def _decorator(name: str, value: str) -> dict[str, str]:
"""Create a QML decorator JSON entry"""
return {"name": name, "value": value}
-def _attribute(node: ast.Attribute) -> Tuple[str, str]:
+def _attribute(node: ast.Attribute) -> tuple[str, str]:
"""Split an attribute."""
return node.value.id, node.attr
-def _name(node: Union[ast.Name, ast.Attribute]) -> str:
+def _name(node: ast.Name | ast.Attribute) -> str:
"""Return the name of something that is either an attribute or a name,
such as base classes or call.func"""
if isinstance(node, ast.Attribute):
return c if c else type
-def _parse_property_kwargs(keywords: List[ast.keyword], prop: PropertyEntry):
+def _parse_property_kwargs(keywords: list[ast.keyword], prop: PropertyEntry):
"""Parse keyword arguments of @Property"""
for k in keywords:
if k.arg == "notify":
prop["notify"] = _name(k.value)
-def _parse_assignment(node: ast.Assign) -> Tuple[Optional[str], Optional[ast.AST]]:
+def _parse_assignment(node: ast.Assign) -> tuple[str | None, ast.AST | None]:
"""Parse an assignment and return a tuple of name, value."""
if len(node.targets) == 1 and isinstance(node.targets[0], ast.Name):
var_name = node.targets[0].id
"""Parse arguments of a Signal call/Slot decorator (type list)."""
result: Arguments = []
for n, arg in enumerate(call.args):
- par_name = f"a{n+1}"
+ par_name = f"a{n + 1}"
par_type = _parse_pyside_type(arg)
result.append({"name": par_name, "type": par_type})
return result
self._context = context
self._json_class_list: ClassList = []
# Property by name, which will be turned into the JSON List later
- self._properties: List[PropertyEntry] = []
- self._signals: List[Signal] = []
+ self._properties: list[PropertyEntry] = []
+ self._signals: list[Signal] = []
self._within_class: bool = False
- self._qt_modules: Set[str] = set()
+ self._qt_modules: set[str] = set()
self._qml_import_name = ""
self._qml_import_major_version = 0
self._qml_import_minor_version = 0
def qml_import_name(self) -> str:
return self._qml_import_name
- def qml_import_version(self) -> Tuple[int, int]:
+ def qml_import_version(self) -> tuple[int, int]:
return (self._qml_import_major_version, self._qml_import_minor_version)
def qt_modules(self):
if bases:
data["superClasses"] = bases
- class_decorators: List[dict] = []
+ class_decorators: list[dict] = []
for d in node.decorator_list:
self._parse_class_decorator(d, class_decorators)
self._parse_function_decorator(node.name, d)
def _parse_class_decorator(self, node: AstDecorator,
- class_decorators: List[dict]):
+ class_decorators: list[dict]):
"""Parse ClassInfo decorators."""
if isinstance(node, ast.Call):
name = _func_name(node)
return -1
def _create_property_entry(self, name: str, type: str,
- getter: Optional[str] = None) -> PropertyEntry:
+ getter: str | None = None) -> PropertyEntry:
"""Create a property JSON entry."""
result: PropertyEntry = {"name": name, "type": type,
"index": len(self._properties)}
def parse_file(file: Path, context: VisitorContext,
- suppress_file: bool = False) -> Optional[Dict]:
+ suppress_file: bool = False) -> dict | None:
"""Parse a file and return its json data"""
ast_tree = MetaObjectDumpVisitor.create_ast(file)
visitor = MetaObjectDumpVisitor(context)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
+from __future__ import annotations
"""
Builds a '.pyproject' file
"""
import sys
import os
-from typing import List, Tuple, Optional
from pathlib import Path
from argparse import ArgumentParser, RawTextHelpFormatter
-from project import (QmlProjectData, check_qml_decorators, is_python_file,
- QMLDIR_FILE, MOD_CMD, METATYPES_JSON_SUFFIX,
- SHADER_SUFFIXES, TRANSLATION_SUFFIX,
- requires_rebuild, run_command, remove_path,
- ProjectData, resolve_project_file, new_project,
- ProjectType, ClOptions)
+from project_lib import (QmlProjectData, check_qml_decorators, is_python_file,
+ QMLDIR_FILE, MOD_CMD, METATYPES_JSON_SUFFIX,
+ SHADER_SUFFIXES, TRANSLATION_SUFFIX,
+ requires_rebuild, run_command, remove_path,
+ ProjectData, resolve_project_file, new_project,
+ ProjectType, ClOptions, DesignStudioProject)
MODE_HELP = """build Builds the project
run Builds the project and runs the first file")
"new-widget": ProjectType.WIDGET}
-def _sort_sources(files: List[Path]) -> List[Path]:
+def _sort_sources(files: list[Path]) -> list[Path]:
"""Sort the sources for building, ensure .qrc is last since it might depend
on generated files."""
"""
Class to wrap the various operations on Project
"""
+
def __init__(self, project_file: Path):
self.project = ProjectData(project_file=project_file)
self.cl_options = ClOptions()
# Files for QML modules using the QmlElement decorators
- self._qml_module_sources: List[Path] = []
- self._qml_module_dir: Optional[Path] = None
- self._qml_dir_file: Optional[Path] = None
+ self._qml_module_sources: list[Path] = []
+ self._qml_module_dir: Path | None = None
+ self._qml_dir_file: Path | None = None
self._qml_project_data = QmlProjectData()
self._qml_module_check()
print(f"{self.project.project_file.name}, {count} QML file(s),"
f" {self._qml_project_data}")
- def _get_artifacts(self, file: Path) -> Tuple[List[Path], Optional[List[str]]]:
+ def _get_artifacts(self, file: Path, output_path: Path | None = None) -> \
+ tuple[list[Path], list[str] | None]:
"""Return path and command for a file's artifact"""
if file.suffix == ".ui": # Qt form files
py_file = f"{file.parent}/ui_{file.stem}.py"
- return ([Path(py_file)], [UIC_CMD, os.fspath(file), "--rc-prefix", "-o", py_file])
+ return [Path(py_file)], [UIC_CMD, os.fspath(file), "--rc-prefix", "-o", py_file]
if file.suffix == ".qrc": # Qt resources
- py_file = f"{file.parent}/rc_{file.stem}.py"
- return ([Path(py_file)], [RCC_CMD, os.fspath(file), "-o", py_file])
+ if not output_path:
+ py_file = f"{file.parent}/rc_{file.stem}.py"
+ else:
+ py_file = str(output_path.resolve())
+ return [Path(py_file)], [RCC_CMD, os.fspath(file), "-o", py_file]
# generate .qmltypes from sources with Qml decorators
if file.suffix == ".py" and file in self._qml_module_sources:
assert self._qml_module_dir
qml_module_dir = os.fspath(self._qml_module_dir)
json_file = f"{qml_module_dir}/{file.stem}{METATYPES_JSON_SUFFIX}"
- return ([Path(json_file)], [MOD_CMD, "-o", json_file, os.fspath(file)])
+ return [Path(json_file)], [MOD_CMD, "-o", json_file, os.fspath(file)]
# Run qmltyperegistrar
if file.name.endswith(METATYPES_JSON_SUFFIX):
assert self._qml_module_dir
os.fspath(qmltypes_file), "-o", os.fspath(cpp_file),
os.fspath(file)]
cmd.extend(self._qml_project_data.registrar_options())
- return ([qmltypes_file, cpp_file], cmd)
+ return [qmltypes_file, cpp_file], cmd
if file.name.endswith(TRANSLATION_SUFFIX):
qm_file = f"{file.parent}/{file.stem}.qm"
cmd = [LRELEASE_CMD, os.fspath(file), "-qm", qm_file]
- return ([Path(qm_file)], cmd)
+ return [Path(qm_file)], cmd
if file.suffix in SHADER_SUFFIXES:
qsb_file = f"{file.parent}/{file.stem}.qsb"
cmd = [QSB_CMD, "-o", qsb_file, os.fspath(file)]
- return ([Path(qsb_file)], cmd)
+ return [Path(qsb_file)], cmd
- return ([], None)
+ return [], None
def _regenerate_qmldir(self):
"""Regenerate the 'qmldir' file."""
for f in self._qml_module_dir.glob("*.qmltypes"):
qf.write(f"typeinfo {f.name}\n")
- def _build_file(self, source: Path):
- """Build an artifact."""
- artifacts, command = self._get_artifacts(source)
+ def _build_file(self, source: Path, output_path: Path | None = None):
+ """Build an artifact if necessary."""
+ artifacts, command = self._get_artifacts(source, output_path)
for artifact in artifacts:
if self.cl_options.force or requires_rebuild([source], artifact):
run_command(command, cwd=self.project.project_file.parent)
self._build_file(artifact) # Recurse for QML (json->qmltypes)
+ def build_design_studio_resources(self):
+ """
+ The resources that need to be compiled are defined in autogen/settings.py
+ """
+ ds_project = DesignStudioProject(self.project.main_file)
+ if (resources_file_path := ds_project.get_resource_file_path()) is None:
+ return
+
+ compiled_resources_file_path = ds_project.get_compiled_resources_file_path()
+ self._build_file(resources_file_path, compiled_resources_file_path)
+
def build(self):
- """Build."""
+ """Build the whole project"""
for sub_project_file in self.project.sub_projects_files:
Project(project_file=sub_project_file).build()
+
if self._qml_module_dir:
self._qml_module_dir.mkdir(exist_ok=True, parents=True)
+
for file in _sort_sources(self.project.files):
self._build_file(file)
+
+ if DesignStudioProject.is_ds_project(self.project.main_file):
+ self.build_design_studio_resources()
+
self._regenerate_qmldir()
def run(self):
first_module_dir = self._qml_module_dir.parts[project_dir_parts]
remove_path(self.project.project_file.parent / first_module_dir)
+ if DesignStudioProject.is_ds_project(self.project.main_file):
+ DesignStudioProject(self.project.main_file).clean()
+
def _qmllint(self):
"""Helper for running qmllint on .qml files (non-recursive)."""
if not self.project.qml_files:
return
source_files = self.project.python_files + self.project.ui_files
- cmd_prefix = [LUPDATE_CMD] + [p.name for p in source_files]
+ project_dir = self.project.project_file.parent
+ cmd_prefix = [LUPDATE_CMD] + [os.fspath(p.relative_to(project_dir)) for p in source_files]
cmd_prefix.append("-ts")
for ts_file in self.project.ts_files:
if requires_rebuild(source_files, ts_file):
cmd = cmd_prefix
cmd.append(ts_file.name)
- run_command(cmd, cwd=self.project.project_file.parent)
+ run_command(cmd, cwd=project_dir)
-if __name__ == "__main__":
- parser = ArgumentParser(description=__doc__, formatter_class=RawTextHelpFormatter)
- parser.add_argument("--quiet", "-q", action="store_true", help="Quiet")
- parser.add_argument("--dry-run", "-n", action="store_true", help="Only print commands")
- parser.add_argument("--force", "-f", action="store_true", help="Force rebuild")
- parser.add_argument("--qml-module", "-Q", action="store_true",
- help="Perform check for QML module")
- mode_choices = ["build", "run", "clean", "qmllint", "deploy", "lupdate"]
- mode_choices.extend(NEW_PROJECT_TYPES.keys())
- parser.add_argument("mode", choices=mode_choices, default="build",
- type=str, help=MODE_HELP)
- parser.add_argument("file", help="Project file", nargs="?", type=str)
-
- options = parser.parse_args()
- cl_options = ClOptions(dry_run=options.dry_run, quiet=options.quiet, force=options.force,
- qml_module=options.qml_module)
-
- mode = options.mode
+def main(mode: str = None, file: str = None, dry_run: bool = False, quiet: bool = False,
+ force: bool = False, qml_module: bool = None):
+ cl_options = ClOptions(dry_run=dry_run, quiet=quiet, # noqa: F841
+ force=force, qml_module=qml_module)
new_project_type = NEW_PROJECT_TYPES.get(mode)
if new_project_type:
- if not options.file:
+ if not file:
print(f"{mode} requires a directory name.", file=sys.stderr)
sys.exit(1)
- sys.exit(new_project(options.file, new_project_type))
+ sys.exit(new_project(file, new_project_type))
- project_file = resolve_project_file(options.file)
+ project_file = resolve_project_file(file)
if not project_file:
- print(f"Cannot determine project_file {options.file}", file=sys.stderr)
+ print(f"Cannot determine project_file {file}", file=sys.stderr)
sys.exit(1)
project = Project(project_file)
if mode == "build":
else:
print(f"Invalid mode {mode}", file=sys.stderr)
sys.exit(1)
+
+
+if __name__ == "__main__":
+ parser = ArgumentParser(description=__doc__, formatter_class=RawTextHelpFormatter)
+ parser.add_argument("--quiet", "-q", action="store_true", help="Quiet")
+ parser.add_argument("--dry-run", "-n", action="store_true", help="Only print commands")
+ parser.add_argument("--force", "-f", action="store_true", help="Force rebuild")
+ parser.add_argument("--qml-module", "-Q", action="store_true",
+ help="Perform check for QML module")
+ mode_choices = ["build", "run", "clean", "qmllint", "deploy", "lupdate"]
+ mode_choices.extend(NEW_PROJECT_TYPES.keys())
+ parser.add_argument("mode", choices=mode_choices, default="build",
+ type=str, help=MODE_HELP)
+
+ # TODO: improve the command structure.
+ # "File" argument is not correct when doing new-... project
+ parser.add_argument("file", help="Project file", nargs="?", type=str)
+
+ args = parser.parse_args()
+ main(args.mode, args.file, args.dry_run, args.quiet, args.force, args.qml_module)
{
- "files": ["project.py", "project/__init__.py", "project/newproject.py",
- "project/project_data.py", "project/utils.py"]
+ "files": ["project.py", "project_lib/__init__.py", "project_lib/design_studio_project.py",
+ "project_lib/newproject.py", "project_lib/project_data.py", "project_lib/utils.py"]
}
+++ /dev/null
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-from dataclasses import dataclass
-
-QTPATHS_CMD = "qtpaths6"
-MOD_CMD = "pyside6-metaobjectdump"
-
-PROJECT_FILE_SUFFIX = ".pyproject"
-QMLDIR_FILE = "qmldir"
-
-QML_IMPORT_NAME = "QML_IMPORT_NAME"
-QML_IMPORT_MAJOR_VERSION = "QML_IMPORT_MAJOR_VERSION"
-QML_IMPORT_MINOR_VERSION = "QML_IMPORT_MINOR_VERSION"
-QT_MODULES = "QT_MODULES"
-
-METATYPES_JSON_SUFFIX = "metatypes.json"
-TRANSLATION_SUFFIX = ".ts"
-SHADER_SUFFIXES = ".vert", ".frag"
-
-
-class Singleton(type):
- _instances = {}
-
- def __call__(cls, *args, **kwargs):
- if cls not in cls._instances:
- cls._instances[cls] = super().__call__(*args, **kwargs)
- return cls._instances[cls]
-
-
-@dataclass(frozen=True)
-class ClOptions(metaclass=Singleton):
- """
- Dataclass to store the cl options that needs to be passed as arguments.
- """
- dry_run: bool
- quiet: bool
- force: bool
- qml_module: bool
-
-
-from .utils import (run_command, requires_rebuild, remove_path, package_dir, qtpaths,
- qt_metatype_json_dir, resolve_project_file)
-from .project_data import (is_python_file, ProjectData, QmlProjectData,
- check_qml_decorators)
-from .newproject import new_project, ProjectType
+++ /dev/null
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-import json
-import os
-import sys
-from enum import Enum
-from pathlib import Path
-from typing import List, Tuple
-
-"""New project generation code."""
-
-
-Project = List[Tuple[str, str]] # tuple of (filename, contents).
-
-
-class ProjectType(Enum):
- WIDGET_FORM = 1
- WIDGET = 2
- QUICK = 3
-
-
-_WIDGET_MAIN = """if __name__ == '__main__':
- app = QApplication(sys.argv)
- window = MainWindow()
- window.show()
- sys.exit(app.exec())
-"""
-
-
-_WIDGET_IMPORTS = """import sys
-from PySide6.QtWidgets import QApplication, QMainWindow
-"""
-
-
-_WIDGET_CLASS_DEFINITION = """class MainWindow(QMainWindow):
- def __init__(self):
- super().__init__()
-"""
-
-
-_WIDGET_SETUP_UI_CODE = """ self._ui = Ui_MainWindow()
- self._ui.setupUi(self)
-"""
-
-
-_MAINWINDOW_FORM = """<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>MainWindow</class>
- <widget class="QMainWindow" name="MainWindow">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>800</width>
- <height>600</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>MainWindow</string>
- </property>
- <widget class="QWidget" name="centralwidget"/>
- <widget class="QMenuBar" name="menubar">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>800</width>
- <height>22</height>
- </rect>
- </property>
- </widget>
- <widget class="QStatusBar" name="statusbar"/>
- </widget>
-</ui>
-"""
-
-
-_QUICK_FORM = """import QtQuick
-import QtQuick.Controls
-
-ApplicationWindow {
- id: window
- width: 1024
- height: 600
- visible: true
-}
-"""
-
-_QUICK_MAIN = """import sys
-from pathlib import Path
-
-from PySide6.QtGui import QGuiApplication
-from PySide6.QtCore import QUrl
-from PySide6.QtQml import QQmlApplicationEngine
-
-
-if __name__ == "__main__":
- app = QGuiApplication()
- engine = QQmlApplicationEngine()
- qml_file = Path(__file__).parent / 'main.qml'
- engine.load(QUrl.fromLocalFile(qml_file))
- if not engine.rootObjects():
- sys.exit(-1)
- exit_code = app.exec()
- del engine
- sys.exit(exit_code)
-"""
-
-
-def _write_project(directory: Path, files: Project):
- """Write out the project."""
- file_list = []
- for file, contents in files:
- (directory / file).write_text(contents)
- print(f"Wrote {directory.name}{os.sep}{file}.")
- file_list.append(file)
- pyproject = {"files": file_list}
- pyproject_file = f"{directory}.pyproject"
- (directory / pyproject_file).write_text(json.dumps(pyproject))
- print(f"Wrote {directory.name}{os.sep}{pyproject_file}.")
-
-
-def _widget_project() -> Project:
- """Create a (form-less) widgets project."""
- main_py = (_WIDGET_IMPORTS + "\n\n" + _WIDGET_CLASS_DEFINITION + "\n\n"
- + _WIDGET_MAIN)
- return [("main.py", main_py)]
-
-
-def _ui_form_project() -> Project:
- """Create a Qt Designer .ui form based widgets project."""
- main_py = (_WIDGET_IMPORTS
- + "\nfrom ui_mainwindow import Ui_MainWindow\n\n\n"
- + _WIDGET_CLASS_DEFINITION + _WIDGET_SETUP_UI_CODE
- + "\n\n" + _WIDGET_MAIN)
- return [("main.py", main_py),
- ("mainwindow.ui", _MAINWINDOW_FORM)]
-
-
-def _qml_project() -> Project:
- """Create a QML project."""
- return [("main.py", _QUICK_MAIN),
- ("main.qml", _QUICK_FORM)]
-
-
-def new_project(directory_s: str,
- project_type: ProjectType = ProjectType.WIDGET_FORM) -> int:
- directory = Path(directory_s)
- if directory.exists():
- print(f"{directory_s} already exists.", file=sys.stderr)
- return -1
- directory.mkdir(parents=True)
-
- if project_type == ProjectType.WIDGET_FORM:
- project = _ui_form_project()
- elif project_type == ProjectType.QUICK:
- project = _qml_project()
- else:
- project = _widget_project()
- _write_project(directory, project)
- if project_type == ProjectType.WIDGET_FORM:
- print(f'Run "pyside6-project build {directory_s}" to build the project')
- print(f'Run "python {directory.name}{os.sep}main.py" to run the project')
- return 0
+++ /dev/null
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-import json
-import os
-import subprocess
-import sys
-from typing import List, Tuple
-from pathlib import Path
-from . import (METATYPES_JSON_SUFFIX, PROJECT_FILE_SUFFIX, TRANSLATION_SUFFIX,
- qt_metatype_json_dir, MOD_CMD, QML_IMPORT_MAJOR_VERSION,
- QML_IMPORT_MINOR_VERSION, QML_IMPORT_NAME, QT_MODULES)
-
-
-def is_python_file(file: Path) -> bool:
- return (file.suffix == ".py"
- or sys.platform == "win32" and file.suffix == ".pyw")
-
-
-class ProjectData:
- def __init__(self, project_file: Path) -> None:
- """Parse the project."""
- self._project_file = project_file
- self._sub_projects_files: List[Path] = []
-
- # All sources except subprojects
- self._files: List[Path] = []
- # QML files
- self._qml_files: List[Path] = []
- # Python files
- self.main_file: Path = None
- self._python_files: List[Path] = []
- # ui files
- self._ui_files: List[Path] = []
- # qrc files
- self._qrc_files: List[Path] = []
- # ts files
- self._ts_files: List[Path] = []
-
- with project_file.open("r") as pyf:
- pyproject = json.load(pyf)
- for f in pyproject["files"]:
- file = Path(project_file.parent / f)
- if file.suffix == PROJECT_FILE_SUFFIX:
- self._sub_projects_files.append(file)
- else:
- self._files.append(file)
- if file.suffix == ".qml":
- self._qml_files.append(file)
- elif is_python_file(file):
- if file.stem == "main":
- self.main_file = file
- self._python_files.append(file)
- elif file.suffix == ".ui":
- self._ui_files.append(file)
- elif file.suffix == ".qrc":
- self._qrc_files.append(file)
- elif file.suffix == TRANSLATION_SUFFIX:
- self._ts_files.append(file)
-
- if not self.main_file:
- self._find_main_file()
-
- @property
- def project_file(self):
- return self._project_file
-
- @property
- def files(self):
- return self._files
-
- @property
- def main_file(self):
- return self._main_file
-
- @main_file.setter
- def main_file(self, main_file):
- self._main_file = main_file
-
- @property
- def python_files(self):
- return self._python_files
-
- @property
- def ui_files(self):
- return self._ui_files
-
- @property
- def qrc_files(self):
- return self._qrc_files
-
- @property
- def qml_files(self):
- return self._qml_files
-
- @property
- def ts_files(self):
- return self._ts_files
-
- @property
- def sub_projects_files(self):
- return self._sub_projects_files
-
- def _find_main_file(self) -> str:
- """Find the entry point file containing the main function"""
-
- def is_main(file):
- return "__main__" in file.read_text(encoding="utf-8")
-
- if not self.main_file:
- for python_file in self.python_files:
- if is_main(python_file):
- self.main_file = python_file
- return str(python_file)
-
- # __main__ not found
- print(
- "Python file with main function not found. Add the file to" f" {self.project_file}",
- file=sys.stderr,
- )
- sys.exit(1)
-
-
-class QmlProjectData:
- """QML relevant project data."""
-
- def __init__(self):
- self._import_name: str = ""
- self._import_major_version: int = 0
- self._import_minor_version: int = 0
- self._qt_modules: List[str] = []
-
- def registrar_options(self):
- result = [
- "--import-name",
- self._import_name,
- "--major-version",
- str(self._import_major_version),
- "--minor-version",
- str(self._import_minor_version),
- ]
- if self._qt_modules:
- # Add Qt modules as foreign types
- foreign_files: List[str] = []
- meta_dir = qt_metatype_json_dir()
- for mod in self._qt_modules:
- mod_id = mod[2:].lower()
- pattern = f"qt6{mod_id}_*"
- if sys.platform != "win32":
- pattern += "_" # qt6core_debug_metatypes.json (Linux)
- pattern += METATYPES_JSON_SUFFIX
- for f in meta_dir.glob(pattern):
- foreign_files.append(os.fspath(f))
- break
- if foreign_files:
- foreign_files_str = ",".join(foreign_files)
- result.append(f"--foreign-types={foreign_files_str}")
- return result
-
- @property
- def import_name(self):
- return self._import_name
-
- @import_name.setter
- def import_name(self, n):
- self._import_name = n
-
- @property
- def import_major_version(self):
- return self._import_major_version
-
- @import_major_version.setter
- def import_major_version(self, v):
- self._import_major_version = v
-
- @property
- def import_minor_version(self):
- return self._import_minor_version
-
- @import_minor_version.setter
- def import_minor_version(self, v):
- self._import_minor_version = v
-
- @property
- def qt_modules(self):
- return self._qt_modules
-
- @qt_modules.setter
- def qt_modules(self, v):
- self._qt_modules = v
-
- def __str__(self) -> str:
- vmaj = self._import_major_version
- vmin = self._import_minor_version
- return f'"{self._import_name}" v{vmaj}.{vmin}'
-
- def __bool__(self) -> bool:
- return len(self._import_name) > 0 and self._import_major_version > 0
-
-
-def _has_qml_decorated_class(class_list: List) -> bool:
- """Check for QML-decorated classes in the moc json output."""
- for d in class_list:
- class_infos = d.get("classInfos")
- if class_infos:
- for e in class_infos:
- if "QML" in e["name"]:
- return True
- return False
-
-
-def check_qml_decorators(py_file: Path) -> Tuple[bool, QmlProjectData]:
- """Check if a Python file has QML-decorated classes by running a moc check
- and return whether a class was found and the QML data."""
- data = None
- try:
- cmd = [MOD_CMD, "--quiet", os.fspath(py_file)]
- with subprocess.Popen(cmd, stdout=subprocess.PIPE) as proc:
- data = json.load(proc.stdout)
- proc.wait()
- except Exception as e:
- t = type(e).__name__
- print(f"{t}: running {MOD_CMD} on {py_file}: {e}", file=sys.stderr)
- sys.exit(1)
-
- qml_project_data = QmlProjectData()
- if not data:
- return (False, qml_project_data) # No classes in file
-
- first = data[0]
- class_list = first["classes"]
- has_class = _has_qml_decorated_class(class_list)
- if has_class:
- v = first.get(QML_IMPORT_NAME)
- if v:
- qml_project_data.import_name = v
- v = first.get(QML_IMPORT_MAJOR_VERSION)
- if v:
- qml_project_data.import_major_version = v
- qml_project_data.import_minor_version = first.get(QML_IMPORT_MINOR_VERSION)
- v = first.get(QT_MODULES)
- if v:
- qml_project_data.qt_modules = v
- return (has_class, qml_project_data)
+++ /dev/null
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-import sys
-import subprocess
-from pathlib import Path
-from typing import List, Dict, Optional
-
-from . import QTPATHS_CMD, PROJECT_FILE_SUFFIX, ClOptions
-
-
-def run_command(command: List[str], cwd: str = None, ignore_fail: bool = False):
- """Run a command observing quiet/dry run"""
- cloptions = ClOptions()
- if not cloptions.quiet or cloptions.dry_run:
- print(" ".join(command))
- if not cloptions.dry_run:
- ex = subprocess.call(command, cwd=cwd)
- if ex != 0 and not ignore_fail:
- sys.exit(ex)
-
-
-def requires_rebuild(sources: List[Path], artifact: Path) -> bool:
- """Returns whether artifact needs to be rebuilt depending on sources"""
- if not artifact.is_file():
- return True
- artifact_mod_time = artifact.stat().st_mtime
- for source in sources:
- if source.stat().st_mtime > artifact_mod_time:
- return True
- return False
-
-
-def _remove_path_recursion(path: Path):
- """Recursion to remove a file or directory."""
- if path.is_file():
- path.unlink()
- elif path.is_dir():
- for item in path.iterdir():
- _remove_path_recursion(item)
- path.rmdir()
-
-
-def remove_path(path: Path):
- """Remove path (file or directory) observing opt_dry_run."""
- cloptions = ClOptions()
- if not path.exists():
- return
- if not cloptions.quiet:
- print(f"Removing {path.name}...")
- if cloptions.dry_run:
- return
- _remove_path_recursion(path)
-
-
-def package_dir() -> Path:
- """Return the PySide6 root."""
- return Path(__file__).resolve().parents[2]
-
-
-_qtpaths_info: Dict[str, str] = {}
-
-
-def qtpaths() -> Dict[str, str]:
- """Run qtpaths and return a dict of values."""
- global _qtpaths_info
- if not _qtpaths_info:
- output = subprocess.check_output([QTPATHS_CMD, "--query"])
- for line in output.decode("utf-8").split("\n"):
- tokens = line.strip().split(":", maxsplit=1) # "Path=C:\..."
- if len(tokens) == 2:
- _qtpaths_info[tokens[0]] = tokens[1]
- return _qtpaths_info
-
-
-_qt_metatype_json_dir: Optional[Path] = None
-
-
-def qt_metatype_json_dir() -> Path:
- """Return the location of the Qt QML metatype files."""
- global _qt_metatype_json_dir
- if not _qt_metatype_json_dir:
- qt_dir = package_dir()
- if sys.platform != "win32":
- qt_dir /= "Qt"
- metatypes_dir = qt_dir / "metatypes"
- if metatypes_dir.is_dir(): # Fully installed case
- _qt_metatype_json_dir = metatypes_dir
- else:
- # Fallback for distro builds/development.
- print(
- f"Falling back to {QTPATHS_CMD} to determine metatypes directory.", file=sys.stderr
- )
- _qt_metatype_json_dir = Path(qtpaths()["QT_INSTALL_ARCHDATA"]) / "metatypes"
- return _qt_metatype_json_dir
-
-
-def resolve_project_file(cmdline: str) -> Optional[Path]:
- """Return the project file from the command line value, either
- from the file argument or directory"""
- project_file = Path(cmdline).resolve() if cmdline else Path.cwd()
- if project_file.is_file():
- return project_file
- if project_file.is_dir():
- for m in project_file.glob(f"*{PROJECT_FILE_SUFFIX}"):
- return m
- return None
--- /dev/null
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+from dataclasses import dataclass
+
+QTPATHS_CMD = "qtpaths6"
+MOD_CMD = "pyside6-metaobjectdump"
+
+PROJECT_FILE_SUFFIX = ".pyproject"
+QMLDIR_FILE = "qmldir"
+
+QML_IMPORT_NAME = "QML_IMPORT_NAME"
+QML_IMPORT_MAJOR_VERSION = "QML_IMPORT_MAJOR_VERSION"
+QML_IMPORT_MINOR_VERSION = "QML_IMPORT_MINOR_VERSION"
+QT_MODULES = "QT_MODULES"
+
+METATYPES_JSON_SUFFIX = "metatypes.json"
+TRANSLATION_SUFFIX = ".ts"
+SHADER_SUFFIXES = ".vert", ".frag"
+
+
+class Singleton(type):
+ _instances = {}
+
+ def __call__(cls, *args, **kwargs):
+ if cls not in cls._instances:
+ cls._instances[cls] = super().__call__(*args, **kwargs)
+ return cls._instances[cls]
+
+
+@dataclass(frozen=True)
+class ClOptions(metaclass=Singleton):
+ """
+ Dataclass to store the cl options that needs to be passed as arguments.
+ """
+ dry_run: bool
+ quiet: bool
+ force: bool
+ qml_module: bool
+
+
+from .utils import (run_command, requires_rebuild, remove_path, package_dir, qtpaths,
+ qt_metatype_json_dir, resolve_project_file)
+from .project_data import (is_python_file, ProjectData, QmlProjectData,
+ check_qml_decorators)
+from .newproject import new_project, ProjectType
+from .design_studio_project import DesignStudioProject
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+import logging
+from pathlib import Path
+from typing import Optional
+
+
+class DesignStudioProject:
+ """
+ Class to handle Design Studio projects. The project structure is as follows:
+ - Python folder
+ - autogen folder
+ - settings.py
+ - resources.py (Compiled resources)
+ - main.py
+ <ProjectName>.qrc (Resources collection file)
+ <ProjectName>.qmlproject
+ <ProjectName>.qmlproject.qtds (should be added to .gitignore)
+ ... Other files and folders ...
+ """
+
+ def __init__(self, main_file: Path):
+ self.main_file = main_file
+ self.project_dir = main_file.parent.parent
+ self.compiled_resources_file = self.main_file.parent / "autogen" / "resources.py"
+
+ @staticmethod
+ def is_ds_project(main_file: Path) -> bool:
+ return bool(*main_file.parent.parent.glob("*.qmlproject"))
+
+ def compiled_resources_available(self) -> bool:
+ """
+ Returns whether the resources of the project have been compiled into a .py file.
+ TODO: Make the resources path configurable. Wait for the pyproject TOML configuration
+ """
+ return self.compiled_resources_file.exists()
+
+ def get_resource_file_path(self) -> Optional[Path]:
+ """
+ Return the path to the *.qrc resources file from the project root folder.
+ If not found, log an error message and return None
+ If multiple files are found, log an error message and return None
+ If a single file is found, return its path
+ """
+ resource_files = list(self.project_dir.glob("*.qrc"))
+ if not resource_files:
+ logging.error("No *.qrc resources file found in the project root folder")
+ return None
+ if len(resource_files) > 1:
+ logging.error("Multiple *.qrc resources files found in the project root folder")
+ return None
+ return resource_files[0]
+
+ def get_compiled_resources_file_path(self) -> Path:
+ """
+ Return the path of the output file generated by compiling the *.qrc resources file
+ """
+ # TODO: make this more robust and configurable. Wait for the pyproject TOML configuration
+ return self.main_file.parent / "autogen" / "resources.py"
+
+ def clean(self):
+ """
+ Remove the compiled resources file if it exists
+ """
+ self.compiled_resources_file.unlink(missing_ok=True)
--- /dev/null
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+import json
+import os
+import sys
+from enum import Enum
+from pathlib import Path
+
+"""New project generation code."""
+
+
+Project = list[tuple[str, str]] # tuple of (filename, contents).
+
+
+class ProjectType(Enum):
+ WIDGET_FORM = 1
+ WIDGET = 2
+ QUICK = 3
+
+
+_WIDGET_MAIN = """if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ window = MainWindow()
+ window.show()
+ sys.exit(app.exec())
+"""
+
+
+_WIDGET_IMPORTS = """import sys
+from PySide6.QtWidgets import QApplication, QMainWindow
+"""
+
+
+_WIDGET_CLASS_DEFINITION = """class MainWindow(QMainWindow):
+ def __init__(self):
+ super().__init__()
+"""
+
+
+_WIDGET_SETUP_UI_CODE = """ self._ui = Ui_MainWindow()
+ self._ui.setupUi(self)
+"""
+
+
+_MAINWINDOW_FORM = """<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>600</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>MainWindow</string>
+ </property>
+ <widget class="QWidget" name="centralwidget"/>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>22</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+</ui>
+"""
+
+
+_QUICK_FORM = """import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+ id: window
+ width: 1024
+ height: 600
+ visible: true
+}
+"""
+
+_QUICK_MAIN = """import sys
+from pathlib import Path
+
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtCore import QUrl
+from PySide6.QtQml import QQmlApplicationEngine
+
+
+if __name__ == "__main__":
+ app = QGuiApplication()
+ engine = QQmlApplicationEngine()
+ qml_file = Path(__file__).parent / 'main.qml'
+ engine.load(QUrl.fromLocalFile(qml_file))
+ if not engine.rootObjects():
+ sys.exit(-1)
+ exit_code = app.exec()
+ del engine
+ sys.exit(exit_code)
+"""
+
+
+def _write_project(directory: Path, files: Project):
+ """Write out the project."""
+ file_list = []
+ for file, contents in files:
+ (directory / file).write_text(contents)
+ print(f"Wrote {directory.name}{os.sep}{file}.")
+ file_list.append(file)
+ pyproject = {"files": file_list}
+ pyproject_file = f"{directory}.pyproject"
+ (directory / pyproject_file).write_text(json.dumps(pyproject))
+ print(f"Wrote {directory.name}{os.sep}{pyproject_file}.")
+
+
+def _widget_project() -> Project:
+ """Create a (form-less) widgets project."""
+ main_py = (_WIDGET_IMPORTS + "\n\n" + _WIDGET_CLASS_DEFINITION + "\n\n"
+ + _WIDGET_MAIN)
+ return [("main.py", main_py)]
+
+
+def _ui_form_project() -> Project:
+ """Create a Qt Designer .ui form based widgets project."""
+ main_py = (_WIDGET_IMPORTS
+ + "\nfrom ui_mainwindow import Ui_MainWindow\n\n\n"
+ + _WIDGET_CLASS_DEFINITION + _WIDGET_SETUP_UI_CODE
+ + "\n\n" + _WIDGET_MAIN)
+ return [("main.py", main_py),
+ ("mainwindow.ui", _MAINWINDOW_FORM)]
+
+
+def _qml_project() -> Project:
+ """Create a QML project."""
+ return [("main.py", _QUICK_MAIN),
+ ("main.qml", _QUICK_FORM)]
+
+
+def new_project(directory_s: str,
+ project_type: ProjectType = ProjectType.WIDGET_FORM) -> int:
+ directory = Path(directory_s)
+ if directory.exists():
+ print(f"{directory_s} already exists.", file=sys.stderr)
+ return -1
+ directory.mkdir(parents=True)
+
+ if project_type == ProjectType.WIDGET_FORM:
+ project = _ui_form_project()
+ elif project_type == ProjectType.QUICK:
+ project = _qml_project()
+ else:
+ project = _widget_project()
+ _write_project(directory, project)
+ if project_type == ProjectType.WIDGET_FORM:
+ print(f'Run "pyside6-project build {directory_s}" to build the project')
+ print(f'Run "python {directory.name}{os.sep}main.py" to run the project')
+ return 0
--- /dev/null
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+import json
+import os
+import subprocess
+import sys
+from pathlib import Path
+from . import (METATYPES_JSON_SUFFIX, PROJECT_FILE_SUFFIX, TRANSLATION_SUFFIX,
+ qt_metatype_json_dir, MOD_CMD, QML_IMPORT_MAJOR_VERSION,
+ QML_IMPORT_MINOR_VERSION, QML_IMPORT_NAME, QT_MODULES)
+
+
+def is_python_file(file: Path) -> bool:
+ return (file.suffix == ".py"
+ or sys.platform == "win32" and file.suffix == ".pyw")
+
+
+class ProjectData:
+ def __init__(self, project_file: Path) -> None:
+ """Parse the project."""
+ self._project_file = project_file.resolve()
+ self._sub_projects_files: list[Path] = []
+
+ # All sources except subprojects
+ self._files: list[Path] = []
+ # QML files
+ self._qml_files: list[Path] = []
+ # Python files
+ self.main_file: Path = None
+ self._python_files: list[Path] = []
+ # ui files
+ self._ui_files: list[Path] = []
+ # qrc files
+ self._qrc_files: list[Path] = []
+ # ts files
+ self._ts_files: list[Path] = []
+
+ with project_file.open("r") as pyf:
+ pyproject = json.load(pyf)
+ for f in pyproject["files"]:
+ file = Path(project_file.parent / f)
+ if file.suffix == PROJECT_FILE_SUFFIX:
+ self._sub_projects_files.append(file)
+ else:
+ self._files.append(file)
+ if file.suffix == ".qml":
+ self._qml_files.append(file)
+ elif is_python_file(file):
+ if file.stem == "main":
+ self.main_file = file
+ self._python_files.append(file)
+ elif file.suffix == ".ui":
+ self._ui_files.append(file)
+ elif file.suffix == ".qrc":
+ self._qrc_files.append(file)
+ elif file.suffix == TRANSLATION_SUFFIX:
+ self._ts_files.append(file)
+
+ if not self.main_file:
+ self._find_main_file()
+
+ @property
+ def project_file(self):
+ return self._project_file
+
+ @property
+ def files(self):
+ return self._files
+
+ @property
+ def main_file(self):
+ return self._main_file
+
+ @main_file.setter
+ def main_file(self, main_file):
+ self._main_file = main_file
+
+ @property
+ def python_files(self):
+ return self._python_files
+
+ @property
+ def ui_files(self):
+ return self._ui_files
+
+ @property
+ def qrc_files(self):
+ return self._qrc_files
+
+ @property
+ def qml_files(self):
+ return self._qml_files
+
+ @property
+ def ts_files(self):
+ return self._ts_files
+
+ @property
+ def sub_projects_files(self):
+ return self._sub_projects_files
+
+ def _find_main_file(self) -> str:
+ """Find the entry point file containing the main function"""
+
+ def is_main(file):
+ return "__main__" in file.read_text(encoding="utf-8")
+
+ if not self.main_file:
+ for python_file in self.python_files:
+ if is_main(python_file):
+ self.main_file = python_file
+ return str(python_file)
+
+ # __main__ not found
+ print(
+ f"Python file with main function not found. Add the file to {self.project_file}",
+ file=sys.stderr,
+ )
+ sys.exit(1)
+
+
+class QmlProjectData:
+ """QML relevant project data."""
+
+ def __init__(self):
+ self._import_name: str = ""
+ self._import_major_version: int = 0
+ self._import_minor_version: int = 0
+ self._qt_modules: list[str] = []
+
+ def registrar_options(self):
+ result = [
+ "--import-name",
+ self._import_name,
+ "--major-version",
+ str(self._import_major_version),
+ "--minor-version",
+ str(self._import_minor_version),
+ ]
+ if self._qt_modules:
+ # Add Qt modules as foreign types
+ foreign_files: list[str] = []
+ meta_dir = qt_metatype_json_dir()
+ for mod in self._qt_modules:
+ mod_id = mod[2:].lower()
+ pattern = f"qt6{mod_id}_*"
+ if sys.platform != "win32":
+ pattern += "_" # qt6core_debug_metatypes.json (Linux)
+ pattern += METATYPES_JSON_SUFFIX
+ for f in meta_dir.glob(pattern):
+ foreign_files.append(os.fspath(f))
+ break
+ if foreign_files:
+ foreign_files_str = ",".join(foreign_files)
+ result.append(f"--foreign-types={foreign_files_str}")
+ return result
+
+ @property
+ def import_name(self):
+ return self._import_name
+
+ @import_name.setter
+ def import_name(self, n):
+ self._import_name = n
+
+ @property
+ def import_major_version(self):
+ return self._import_major_version
+
+ @import_major_version.setter
+ def import_major_version(self, v):
+ self._import_major_version = v
+
+ @property
+ def import_minor_version(self):
+ return self._import_minor_version
+
+ @import_minor_version.setter
+ def import_minor_version(self, v):
+ self._import_minor_version = v
+
+ @property
+ def qt_modules(self):
+ return self._qt_modules
+
+ @qt_modules.setter
+ def qt_modules(self, v):
+ self._qt_modules = v
+
+ def __str__(self) -> str:
+ vmaj = self._import_major_version
+ vmin = self._import_minor_version
+ return f'"{self._import_name}" v{vmaj}.{vmin}'
+
+ def __bool__(self) -> bool:
+ return len(self._import_name) > 0 and self._import_major_version > 0
+
+
+def _has_qml_decorated_class(class_list: list) -> bool:
+ """Check for QML-decorated classes in the moc json output."""
+ for d in class_list:
+ class_infos = d.get("classInfos")
+ if class_infos:
+ for e in class_infos:
+ if "QML" in e["name"]:
+ return True
+ return False
+
+
+def check_qml_decorators(py_file: Path) -> tuple[bool, QmlProjectData]:
+ """Check if a Python file has QML-decorated classes by running a moc check
+ and return whether a class was found and the QML data."""
+ data = None
+ try:
+ cmd = [MOD_CMD, "--quiet", os.fspath(py_file)]
+ with subprocess.Popen(cmd, stdout=subprocess.PIPE) as proc:
+ data = json.load(proc.stdout)
+ proc.wait()
+ except Exception as e:
+ t = type(e).__name__
+ print(f"{t}: running {MOD_CMD} on {py_file}: {e}", file=sys.stderr)
+ sys.exit(1)
+
+ qml_project_data = QmlProjectData()
+ if not data:
+ return (False, qml_project_data) # No classes in file
+
+ first = data[0]
+ class_list = first["classes"]
+ has_class = _has_qml_decorated_class(class_list)
+ if has_class:
+ v = first.get(QML_IMPORT_NAME)
+ if v:
+ qml_project_data.import_name = v
+ v = first.get(QML_IMPORT_MAJOR_VERSION)
+ if v:
+ qml_project_data.import_major_version = v
+ qml_project_data.import_minor_version = first.get(QML_IMPORT_MINOR_VERSION)
+ v = first.get(QT_MODULES)
+ if v:
+ qml_project_data.qt_modules = v
+ return (has_class, qml_project_data)
--- /dev/null
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+import sys
+import subprocess
+from pathlib import Path
+import xml.etree.ElementTree as ET
+
+from . import QTPATHS_CMD, PROJECT_FILE_SUFFIX, ClOptions
+
+
+def run_command(command: list[str], cwd: str = None, ignore_fail: bool = False):
+ """Run a command observing quiet/dry run"""
+ cloptions = ClOptions()
+ if not cloptions.quiet or cloptions.dry_run:
+ print(" ".join(command))
+ if not cloptions.dry_run:
+ ex = subprocess.call(command, cwd=cwd)
+ if ex != 0 and not ignore_fail:
+ sys.exit(ex)
+
+
+def qrc_file_requires_rebuild(resources_file_path: Path, compiled_resources_path: Path) -> bool:
+ """Returns whether a compiled qrc file needs to be rebuilt based on the files that references"""
+ root_element = ET.parse(resources_file_path).getroot()
+ project_root = resources_file_path.parent
+
+ files = [project_root / file.text for file in root_element.findall(".//file")]
+
+ compiled_resources_time = compiled_resources_path.stat().st_mtime
+ # If any of the resource files has been modified after the compiled qrc file, the compiled qrc
+ # file needs to be rebuilt
+ if any(file.is_file() and file.stat().st_mtime > compiled_resources_time for file in files):
+ return True
+ return False
+
+
+def requires_rebuild(sources: list[Path], artifact: Path) -> bool:
+ """Returns whether artifact needs to be rebuilt depending on sources"""
+ if not artifact.is_file():
+ return True
+
+ artifact_mod_time = artifact.stat().st_mtime
+ for source in sources:
+ if source.stat().st_mtime > artifact_mod_time:
+ return True
+ # The .qrc file references other files that might have changed
+ if source.suffix == '.qrc' and qrc_file_requires_rebuild(source, artifact):
+ return True
+ return False
+
+
+def _remove_path_recursion(path: Path):
+ """Recursion to remove a file or directory."""
+ if path.is_file():
+ path.unlink()
+ elif path.is_dir():
+ for item in path.iterdir():
+ _remove_path_recursion(item)
+ path.rmdir()
+
+
+def remove_path(path: Path):
+ """Remove path (file or directory) observing opt_dry_run."""
+ cloptions = ClOptions()
+ if not path.exists():
+ return
+ if not cloptions.quiet:
+ print(f"Removing {path.name}...")
+ if cloptions.dry_run:
+ return
+ _remove_path_recursion(path)
+
+
+def package_dir() -> Path:
+ """Return the PySide6 root."""
+ return Path(__file__).resolve().parents[2]
+
+
+_qtpaths_info: dict[str, str] = {}
+
+
+def qtpaths() -> dict[str, str]:
+ """Run qtpaths and return a dict of values."""
+ global _qtpaths_info
+ if not _qtpaths_info:
+ output = subprocess.check_output([QTPATHS_CMD, "--query"])
+ for line in output.decode("utf-8").split("\n"):
+ tokens = line.strip().split(":", maxsplit=1) # "Path=C:\..."
+ if len(tokens) == 2:
+ _qtpaths_info[tokens[0]] = tokens[1]
+ return _qtpaths_info
+
+
+_qt_metatype_json_dir: Path | None = None
+
+
+def qt_metatype_json_dir() -> Path:
+ """Return the location of the Qt QML metatype files."""
+ global _qt_metatype_json_dir
+ if not _qt_metatype_json_dir:
+ qt_dir = package_dir()
+ if sys.platform != "win32":
+ qt_dir /= "Qt"
+ metatypes_dir = qt_dir / "metatypes"
+ if metatypes_dir.is_dir(): # Fully installed case
+ _qt_metatype_json_dir = metatypes_dir
+ else:
+ # Fallback for distro builds/development.
+ print(
+ f"Falling back to {QTPATHS_CMD} to determine metatypes directory.", file=sys.stderr
+ )
+ _qt_metatype_json_dir = Path(qtpaths()["QT_INSTALL_ARCHDATA"]) / "metatypes"
+ return _qt_metatype_json_dir
+
+
+def resolve_project_file(cmdline: str) -> Path | None:
+ """Return the project file from the command line value, either
+ from the file argument or directory"""
+ project_file = Path(cmdline).resolve() if cmdline else Path.cwd()
+ if project_file.is_file():
+ return project_file
+ if project_file.is_dir():
+ for m in project_file.glob(f"*{PROJECT_FILE_SUFFIX}"):
+ return m
+ return None
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import importlib
import os
def android_deploy():
- if not sys.platform == "linux":
- print("pyside6-android-deploy only works from a Linux host")
+ if sys.platform == "win32":
+ print("pyside6-android-deploy only works from a Unix host and not a Windows host",
+ file=sys.stderr)
else:
android_requirements_file = Path(__file__).parent / "requirements-android.txt"
with open(android_requirements_file, 'r', encoding='UTF-8') as file:
qt_tool_wrapper("balsamui", sys.argv[1:])
+def svgtoqml():
+ qt_tool_wrapper("svgtoqml", sys.argv[1:])
+
+
if __name__ == "__main__":
main()
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""pyside6-qml tool implementation. This tool mimics the capabilities of qml runtime utility
for python and enables quick protyping with python modules"""
import os
from pathlib import Path
from pprint import pprint
-from typing import List, Set
from PySide6.QtCore import QCoreApplication, Qt, QLibraryInfo, QUrl, SignalInstance
from PySide6.QtGui import QGuiApplication, QSurfaceFormat
from PySide6.QtWidgets import QApplication
-def import_qml_modules(qml_parent_path: Path, module_paths: List[Path] = []):
+def import_qml_modules(qml_parent_path: Path, module_paths: list[Path] = []):
'''
Import all the python modules in the qml_parent_path. This way all the classes
containing the @QmlElement/@QmlNamedElement are also imported
elif module_path.exists() and module_path.suffix == ".py":
search_file_paths.append(module_path)
- def import_module(import_module_paths: Set[Path]):
+ def import_module(import_module_paths: set[Path]):
"""Import the modules in 'import_module_paths'"""
for module_path in import_module_paths:
module_name = module_path.name[:-3]
if args.config == "resizeToItem":
logging.info("qml: Not a QQuickview item. resizeToItem is done by default")
- sys.exit(app.exec())
+ exit_code = app.exec()
+ del engine
+ sys.exit(exit_code)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import logging
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""Tool to dump a Python AST"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""C++ formatting helper functions and formatter class"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""Helper to dump AST nodes for debugging"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""Provides some type information on Qt classes"""
_QT_CLASS_FLAGS = {
- "QBrush": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
- "QGradient": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
- "QIcon": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
+ # QtCore
+ "QCoreApplication": ClassFlag.INSTANTIATE_ON_STACK,
+ "QFile": ClassFlag.PASS_BY_REF | ClassFlag.INSTANTIATE_ON_STACK,
+ "QFileInfo": ClassFlag.INSTANTIATE_ON_STACK,
"QLine": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
"QLineF": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
- "QPixmap": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
+ "QModelIndex": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
+ "QPoint": ClassFlag.PASS_BY_VALUE | ClassFlag.INSTANTIATE_ON_STACK,
"QPointF": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
"QRect": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
"QRectF": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
+ "QSaveFile": ClassFlag.INSTANTIATE_ON_STACK,
+ "QSettings": ClassFlag.PASS_BY_REF | ClassFlag.INSTANTIATE_ON_STACK,
+ "QSize": ClassFlag.PASS_BY_VALUE | ClassFlag.INSTANTIATE_ON_STACK,
"QSizeF": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
"QString": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
- "QFile": ClassFlag.PASS_BY_REF | ClassFlag.INSTANTIATE_ON_STACK,
- "QSettings": ClassFlag.PASS_BY_REF | ClassFlag.INSTANTIATE_ON_STACK,
"QTextStream": ClassFlag.PASS_BY_REF | ClassFlag.INSTANTIATE_ON_STACK,
+ # QtGui
+ "QBrush": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
"QColor": ClassFlag.PASS_BY_VALUE | ClassFlag.INSTANTIATE_ON_STACK,
- "QPoint": ClassFlag.PASS_BY_VALUE | ClassFlag.INSTANTIATE_ON_STACK,
- "QSize": ClassFlag.PASS_BY_VALUE | ClassFlag.INSTANTIATE_ON_STACK,
+ "QGradient": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
+ "QGuiApplication": ClassFlag.INSTANTIATE_ON_STACK,
+ "QIcon": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
+ "QPainter": ClassFlag.INSTANTIATE_ON_STACK,
+ "QPen": ClassFlag.INSTANTIATE_ON_STACK,
+ "QPixmap": ClassFlag.PASS_BY_CONSTREF | ClassFlag.INSTANTIATE_ON_STACK,
+ # QtWidgets
"QApplication": ClassFlag.INSTANTIATE_ON_STACK,
"QColorDialog": ClassFlag.INSTANTIATE_ON_STACK,
- "QCoreApplication": ClassFlag.INSTANTIATE_ON_STACK,
"QFileDialog": ClassFlag.INSTANTIATE_ON_STACK,
- "QFileInfo": ClassFlag.INSTANTIATE_ON_STACK,
"QFontDialog": ClassFlag.INSTANTIATE_ON_STACK,
- "QGuiApplication": ClassFlag.INSTANTIATE_ON_STACK,
"QMessageBox": ClassFlag.INSTANTIATE_ON_STACK,
- "QPainter": ClassFlag.INSTANTIATE_ON_STACK,
- "QPen": ClassFlag.INSTANTIATE_ON_STACK,
+ # QtQml
"QQmlApplicationEngine": ClassFlag.INSTANTIATE_ON_STACK,
"QQmlComponent": ClassFlag.INSTANTIATE_ON_STACK,
"QQmlEngine": ClassFlag.INSTANTIATE_ON_STACK,
- "QQuickView": ClassFlag.INSTANTIATE_ON_STACK,
- "QSaveFile": ClassFlag.INSTANTIATE_ON_STACK
+ # QtQuick
+ "QQuickView": ClassFlag.INSTANTIATE_ON_STACK
}
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import subprocess
import tempfile
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""Tool to dump Python Tokens"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""AST visitor printing out C++"""
set(pyside_MAJOR_VERSION "6")
-set(pyside_MINOR_VERSION "7")
-set(pyside_MICRO_VERSION "2")
+set(pyside_MINOR_VERSION "8")
+set(pyside_MICRO_VERSION "2.1")
set(pyside_PRE_RELEASE_VERSION_TYPE "")
set(pyside_PRE_RELEASE_VERSION "")
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.Qt3DAnimation"
+<typesystem package="PySide6.Qt3DAnimation" doc-package="PySide6.Qt3D"
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="Qt3DRender/typesystem_3drender.xml" generate="no"/>
<namespace-type name="Qt3DAnimation">
<object-type name="QAdditiveClipBlend"/>
<object-type name="QAnimationAspect"/>
<object-type name="QAnimationCallback">
- <enum-type name="Flag"/>
+ <enum-type name="Flag" flags="Flags"/>
</object-type>
<object-type name="QAnimationClip"/>
<object-type name="QAnimationClipLoader">
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.Qt3DCore"
+<typesystem package="PySide6.Qt3DCore" doc-package="PySide6.Qt3D" doc-mode="flat"
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<smart-pointer-type name="QSharedPointer" type="shared" getter="data"
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.Qt3DExtras"
+<typesystem package="PySide6.Qt3DExtras" doc-package="PySide6.Qt3D" doc-mode="flat"
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="Qt3DRender/typesystem_3drender.xml" generate="no"/>
<namespace-type name="Qt3DExtras">
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.Qt3DInput"
+<typesystem package="PySide6.Qt3DInput" doc-package="PySide6.Qt3D" doc-mode="flat"
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="Qt3DCore/typesystem_3dcore.xml" generate="no"/>
<namespace-type name="Qt3DInput">
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.Qt3DLogic"
+<typesystem package="PySide6.Qt3DLogic" doc-package="PySide6.Qt3D" doc-mode="flat"
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="Qt3DCore/typesystem_3dcore.xml" generate="no"/>
<namespace-type name="Qt3DLogic">
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.Qt3DRender"
+<typesystem package="PySide6.Qt3DRender" doc-package="PySide6.Qt3D" doc-mode="flat"
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="Qt3DCore/typesystem_3dcore.xml" generate="no"/>
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
from .events import (
QAsyncioEventLoopPolicy, QAsyncioEventLoop, QAsyncioHandle, QAsyncioTimerHandle
from .futures import QAsyncioFuture
from .tasks import QAsyncioTask
+from typing import Coroutine, Any
+
import asyncio
-import typing
__all__ = [
"QAsyncioEventLoopPolicy", "QAsyncioEventLoop",
]
-def run(coro: typing.Optional[typing.Coroutine] = None,
- keep_running: bool = True,
- quit_qapp: bool = True, *,
- handle_sigint: bool = False,
- debug: typing.Optional[bool] = None) -> typing.Any:
- """Run the QtAsyncio event loop."""
+def run(coro: Coroutine | None = None,
+ keep_running: bool = True, quit_qapp: bool = True, *, handle_sigint: bool = False,
+ debug: bool | None = None) -> Any:
+ """
+ Run the QtAsyncio event loop.
+
+ If there is no instance of a QCoreApplication, QGuiApplication or
+ QApplication yet, a new instance of QCoreApplication is created.
+
+ :param coro: The coroutine to run. Optional if keep_running is
+ True.
+ :param keep_running: If True, QtAsyncio (the asyncio event loop) will
+ continue running after the coroutine finished, or
+ run "forever" if no coroutine was provided.
+ If False, QtAsyncio will stop after the
+ coroutine finished. A coroutine must be provided if
+ this argument is set to False.
+ :param quit_qapp: If True, the QCoreApplication will quit when
+ QtAsyncio (the asyncio event loop) stops.
+ If False, the QCoreApplication will remain active
+ after QtAsyncio stops, and can continue to be used.
+ :param handle_sigint: If True, the SIGINT signal will be handled by the
+ event loop, causing it to stop.
+ :param debug: If True, the event loop will run in debug mode.
+ If False, the event loop will run in normal mode.
+ If None, the default behavior is used.
+ """
# Event loop policies are expected to be deprecated with Python 3.13, with
# subsequent removal in Python 3.15. At that point, part of the current
ret = asyncio.run(coro, debug=debug)
else:
exc = RuntimeError(
- "QtAsyncio was set to keep running after the coroutine "
+ "QtAsyncio was set not to keep running after the coroutine "
"finished, but no coroutine was provided.")
asyncio.set_event_loop_policy(default_policy)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
from PySide6.QtCore import (QCoreApplication, QDateTime, QDeadlineTimer,
QEventLoop, QObject, QTimer, QThread, Slot)
from . import futures
from . import tasks
+from typing import Any, Callable
+
import asyncio
import collections.abc
import concurrent.futures
import signal
import socket
import subprocess
-import typing
import warnings
__all__ = [
the actual callable for the executor into this new event loop.
"""
- def __init__(self, func: typing.Callable, *args: typing.Tuple) -> None:
+ def __init__(self, func: Callable, *args: tuple) -> None:
super().__init__()
self._loop: QEventLoop
self._func = func
self._loop = QEventLoop()
asyncio.events._set_running_loop(self._loop)
- QTimer.singleShot(0, self._loop, lambda: self._cb())
+ # The do() function will always be executed from the new executor
+ # thread and never from outside, so using the overload without the
+ # context argument is sufficient.
+ QTimer.singleShot(0, lambda: self._cb())
self._loop.exec()
if self._exception is not None:
https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553
"""
def __init__(self,
- application: typing.Optional[QCoreApplication] = None,
quit_qapp: bool = True,
handle_sigint: bool = False) -> None:
super().__init__()
- if application is None:
- if QCoreApplication.instance() is None:
- application = QCoreApplication()
- else:
- application = QCoreApplication.instance()
- self._application: QCoreApplication = application # type: ignore[assignment]
+ self._application = QCoreApplication.instance() or QCoreApplication()
# Configure whether the QCoreApplication at the core of QtAsyncio
# should be shut down when asyncio finishes. A special case where one
# this instance is shut down every time.
self._quit_qapp = quit_qapp
- self._event_loop: typing.Optional[asyncio.AbstractEventLoop] = None
+ self._event_loop: asyncio.AbstractEventLoop | None = None
if handle_sigint:
signal.signal(signal.SIGINT, signal.SIG_DFL)
self._event_loop = QAsyncioEventLoop(self._application, quit_qapp=self._quit_qapp)
return self._event_loop
- def set_event_loop(self, loop: typing.Optional[asyncio.AbstractEventLoop]) -> None:
+ def set_event_loop(self, loop: asyncio.AbstractEventLoop | None) -> None:
self._event_loop = loop
def new_event_loop(self) -> asyncio.AbstractEventLoop:
self._quit_from_outside = False
# A set of all asynchronous generators that are currently running.
- self._asyncgens: typing.Set[collections.abc.AsyncGenerator] = set()
+ self._asyncgens: set[collections.abc.AsyncGenerator] = set()
# Starting with Python 3.11, this must be an instance of
# ThreadPoolExecutor.
# asynchonrous generator raises an exception when closed, and two, if
# an exception is raised during the execution of a task. Currently, the
# default exception handler just prints the exception to the console.
- self._exception_handler: typing.Optional[typing.Callable] = self.default_exception_handler
+ self._exception_handler: Callable | None = self.default_exception_handler
# The task factory, if set with set_task_factory(). Otherwise, a new
# task is created with the QAsyncioTask constructor.
- self._task_factory: typing.Optional[typing.Callable] = None
+ self._task_factory: Callable | None = None
# The future that is currently being awaited with run_until_complete().
- self._future_to_complete: typing.Optional[futures.QAsyncioFuture] = None
+ self._future_to_complete: futures.QAsyncioFuture | None = None
self._debug = bool(os.getenv("PYTHONASYNCIODEBUG", False))
future.get_loop().stop()
def run_until_complete(self,
- future: futures.QAsyncioFuture) -> typing.Any: # type: ignore[override]
+ future: futures.QAsyncioFuture) -> Any: # type: ignore[override]
if self.is_closed():
raise RuntimeError("Event loop is closed")
if self.is_running():
self._asyncgens.clear()
async def shutdown_default_executor(self, # type: ignore[override]
- timeout: typing.Union[int, float, None] = None) -> None:
+ timeout: int | float | None = None) -> None:
shutdown_successful = False
if timeout is not None:
deadline_timer = QDeadlineTimer(int(timeout * 1000))
# Scheduling callbacks
- def _call_soon_impl(self, callback: typing.Callable, *args: typing.Any,
- context: typing.Optional[contextvars.Context] = None,
- is_threadsafe: typing.Optional[bool] = False) -> asyncio.Handle:
+ def _call_soon_impl(self, callback: Callable, *args: Any,
+ context: contextvars.Context | None = None,
+ is_threadsafe: bool | None = False) -> asyncio.Handle:
return self._call_later_impl(0, callback, *args, context=context,
is_threadsafe=is_threadsafe)
- def call_soon(self, callback: typing.Callable, *args: typing.Any,
- context: typing.Optional[contextvars.Context] = None) -> asyncio.Handle:
+ def call_soon(self, callback: Callable, *args: Any,
+ context: contextvars.Context | None = None) -> asyncio.Handle:
return self._call_soon_impl(callback, *args, context=context, is_threadsafe=False)
- def call_soon_threadsafe(self, callback: typing.Callable, *args: typing.Any,
- context:
- typing.Optional[contextvars.Context] = None) -> asyncio.Handle:
+ def call_soon_threadsafe(self, callback: Callable, *args: Any,
+ context: contextvars.Context | None = None) -> asyncio.Handle:
if self.is_closed():
raise RuntimeError("Event loop is closed")
if context is None:
context = contextvars.copy_context()
return self._call_soon_impl(callback, *args, context=context, is_threadsafe=True)
- def _call_later_impl(self, delay: typing.Union[int, float],
- callback: typing.Callable, *args: typing.Any,
- context: typing.Optional[contextvars.Context] = None,
- is_threadsafe: typing.Optional[bool] = False) -> asyncio.TimerHandle:
+ def _call_later_impl(self, delay: int | float, callback: Callable, *args: Any,
+ context: contextvars.Context | None = None,
+ is_threadsafe: bool | None = False) -> asyncio.TimerHandle:
if not isinstance(delay, (int, float)):
raise TypeError("delay must be an int or float")
return self._call_at_impl(self.time() + delay, callback, *args, context=context,
is_threadsafe=is_threadsafe)
- def call_later(self, delay: typing.Union[int, float],
- callback: typing.Callable, *args: typing.Any,
- context: typing.Optional[contextvars.Context] = None) -> asyncio.TimerHandle:
+ def call_later(self, delay: int | float, callback: Callable, *args: Any,
+ context: contextvars.Context | None = None) -> asyncio.TimerHandle:
return self._call_later_impl(delay, callback, *args, context=context, is_threadsafe=False)
- def _call_at_impl(self, when: typing.Union[int, float],
- callback: typing.Callable, *args: typing.Any,
- context: typing.Optional[contextvars.Context] = None,
- is_threadsafe: typing.Optional[bool] = False) -> asyncio.TimerHandle:
+ def _call_at_impl(self, when: int | float, callback: Callable, *args: Any,
+ context: contextvars.Context | None = None,
+ is_threadsafe: bool | None = False) -> asyncio.TimerHandle:
""" All call_at() and call_later() methods map to this method. """
if not isinstance(when, (int, float)):
raise TypeError("when must be an int or float")
return QAsyncioTimerHandle(when, callback, args, self, context, is_threadsafe=is_threadsafe)
- def call_at(self, when: typing.Union[int, float],
- callback: typing.Callable, *args: typing.Any,
- context: typing.Optional[contextvars.Context] = None) -> asyncio.TimerHandle:
+ def call_at(self, when: int | float, callback: Callable, *args: Any,
+ context: contextvars.Context | None = None) -> asyncio.TimerHandle:
return self._call_at_impl(when, callback, *args, context=context, is_threadsafe=False)
def time(self) -> float:
return futures.QAsyncioFuture(loop=self)
def create_task(self, # type: ignore[override]
- coro: typing.Union[collections.abc.Generator, collections.abc.Coroutine],
- *, name: typing.Optional[str] = None,
- context: typing.Optional[contextvars.Context] = None) -> tasks.QAsyncioTask:
+ coro: collections.abc.Generator | collections.abc.Coroutine,
+ *, name: str | None = None,
+ context: contextvars.Context | None = None) -> tasks.QAsyncioTask:
if self._task_factory is None:
task = tasks.QAsyncioTask(coro, loop=self, name=name, context=context)
else:
return task
- def set_task_factory(self, factory: typing.Optional[typing.Callable]) -> None:
+ def set_task_factory(self, factory: Callable | None) -> None:
if factory is not None and not callable(factory):
raise TypeError("The task factory must be a callable or None")
self._task_factory = factory
- def get_task_factory(self) -> typing.Optional[typing.Callable]:
+ def get_task_factory(self) -> Callable | None:
return self._task_factory
# Opening network connections
ssl_handshake_timeout=None,
ssl_shutdown_timeout=None,
happy_eyeballs_delay=None, interleave=None):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.create_connection() is not implemented yet")
async def create_datagram_endpoint(self, protocol_factory,
local_addr=None, remote_addr=None, *,
family=0, proto=0, flags=0,
reuse_address=None, reuse_port=None,
allow_broadcast=None, sock=None):
- raise NotImplementedError
+ raise NotImplementedError(
+ "QAsyncioEventLoop.create_datagram_endpoint() is not implemented yet")
async def create_unix_connection(
self, protocol_factory, path=None, *,
server_hostname=None,
ssl_handshake_timeout=None,
ssl_shutdown_timeout=None):
- raise NotImplementedError
+ raise NotImplementedError(
+ "QAsyncioEventLoop.create_unix_connection() is not implemented yet")
# Creating network servers
ssl_handshake_timeout=None,
ssl_shutdown_timeout=None,
start_serving=True):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.create_server() is not implemented yet")
async def create_unix_server(
self, protocol_factory, path=None, *,
ssl_handshake_timeout=None,
ssl_shutdown_timeout=None,
start_serving=True):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.create_unix_server() is not implemented yet")
async def connect_accepted_socket(
self, protocol_factory, sock,
*, ssl=None,
ssl_handshake_timeout=None,
ssl_shutdown_timeout=None):
- raise NotImplementedError
+ raise NotImplementedError(
+ "QAsyncioEventLoop.connect_accepted_socket() is not implemented yet")
# Transferring files
async def sendfile(self, transport, file, offset=0, count=None,
*, fallback=True):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.sendfile() is not implemented yet")
# TLS Upgrade
server_hostname=None,
ssl_handshake_timeout=None,
ssl_shutdown_timeout=None):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.start_tls() is not implemented yet")
# Watching file descriptors
def add_reader(self, fd, callback, *args):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.add_reader() is not implemented yet")
def remove_reader(self, fd):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.remove_reader() is not implemented yet")
def add_writer(self, fd, callback, *args):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.add_writer() is not implemented yet")
def remove_writer(self, fd):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.remove_writer() is not implemented yet")
# Working with socket objects directly
async def sock_recv(self, sock, nbytes):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.sock_recv() is not implemented yet")
async def sock_recv_into(self, sock, buf):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.sock_recv_into() is not implemented yet")
async def sock_recvfrom(self, sock, bufsize):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.sock_recvfrom() is not implemented yet")
async def sock_recvfrom_into(self, sock, buf, nbytes=0):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.sock_recvfrom_into() is not implemented yet")
async def sock_sendall(self, sock, data):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.sock_sendall() is not implemented yet")
async def sock_sendto(self, sock, data, address):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.sock_sendto() is not implemented yet")
async def sock_connect(self, sock, address):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.sock_connect() is not implemented yet")
async def sock_accept(self, sock):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.sock_accept() is not implemented yet")
async def sock_sendfile(self, sock, file, offset=0, count=None, *,
fallback=None):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.sock_sendfile() is not implemented yet")
# DNS
async def getaddrinfo(self, host, port, *,
family=0, type=0, proto=0, flags=0):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.getaddrinfo() is not implemented yet")
async def getnameinfo(self, sockaddr, flags=0):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.getnameinfo() is not implemented yet")
# Working with pipes
async def connect_read_pipe(self, protocol_factory, pipe):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.connect_read_pipe() is not implemented yet")
async def connect_write_pipe(self, protocol_factory, pipe):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.connect_write_pipe() is not implemented yet")
# Unix signals
def add_signal_handler(self, sig, callback, *args):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.add_signal_handler() is not implemented yet")
def remove_signal_handler(self, sig):
- raise NotImplementedError
+ raise NotImplementedError(
+ "QAsyncioEventLoop.remove_signal_handler() is not implemented yet")
# Executing code in thread or process pools
def run_in_executor(self,
- executor: typing.Optional[concurrent.futures.ThreadPoolExecutor],
- func: typing.Callable, *args: typing.Tuple) -> asyncio.futures.Future:
+ executor: concurrent.futures.ThreadPoolExecutor | None,
+ func: Callable, *args: tuple) -> asyncio.futures.Future:
if self.is_closed():
raise RuntimeError("Event loop is closed")
if executor is None:
)
def set_default_executor(self,
- executor: typing.Optional[
- concurrent.futures.ThreadPoolExecutor]) -> None:
+ executor: concurrent.futures.ThreadPoolExecutor | None) -> None:
if not isinstance(executor, concurrent.futures.ThreadPoolExecutor):
raise TypeError("The executor must be a ThreadPoolExecutor")
self._default_executor = executor
# Error Handling API
- def set_exception_handler(self, handler: typing.Optional[typing.Callable]) -> None:
+ def set_exception_handler(self, handler: Callable | None) -> None:
if handler is not None and not callable(handler):
raise TypeError("The handler must be a callable or None")
self._exception_handler = handler
- def get_exception_handler(self) -> typing.Optional[typing.Callable]:
+ def get_exception_handler(self) -> Callable | None:
return self._exception_handler
- def default_exception_handler(self, context: typing.Dict[str, typing.Any]) -> None:
+ def default_exception_handler(self, context: dict[str, Any]) -> None:
# TODO
if context["message"]:
print(context["message"])
- def call_exception_handler(self, context: typing.Dict[str, typing.Any]) -> None:
+ def call_exception_handler(self, context: dict[str, Any]) -> None:
if self._exception_handler is not None:
self._exception_handler(context)
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
**kwargs):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.subprocess_exec() is not implemented yet")
async def subprocess_shell(self, protocol_factory, cmd, *,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
**kwargs):
- raise NotImplementedError
+ raise NotImplementedError("QAsyncioEventLoop.subprocess_shell() is not implemented yet")
class QAsyncioHandle():
CANCELLED = enum.auto()
DONE = enum.auto()
- def __init__(self, callback: typing.Callable, args: typing.Tuple,
- loop: QAsyncioEventLoop, context: typing.Optional[contextvars.Context],
- is_threadsafe: typing.Optional[bool] = False) -> None:
+ def __init__(self, callback: Callable, args: tuple,
+ loop: QAsyncioEventLoop, context: contextvars.Context | None,
+ is_threadsafe: bool | None = False) -> None:
self._callback = callback
self._args = args
self._loop = loop
def _start(self) -> None:
self._schedule_event(self._timeout, lambda: self._cb())
- def _schedule_event(self, timeout: int, func: typing.Callable) -> None:
+ def _schedule_event(self, timeout: int, func: Callable) -> None:
# Do not schedule events from asyncio when the app is quit from outside
# the event loop, as this would cause events to be enqueued after the
# event loop was destroyed.
class QAsyncioTimerHandle(QAsyncioHandle, asyncio.TimerHandle):
- def __init__(self, when: float, callback: typing.Callable, args: typing.Tuple,
- loop: QAsyncioEventLoop, context: typing.Optional[contextvars.Context],
- is_threadsafe: typing.Optional[bool] = False) -> None:
+ def __init__(self, when: float, callback: Callable, args: tuple,
+ loop: QAsyncioEventLoop, context: contextvars.Context | None,
+ is_threadsafe: bool | None = False) -> None:
QAsyncioHandle.__init__(self, callback, args, loop, context, is_threadsafe)
self._when = when
time = self._loop.time()
+
+ # PYSIDE-2644: Timeouts should be rounded up or down instead of only up
+ # as happens with int(). Otherwise, a timeout of e.g. 0.9 would be
+ # handled as 0, where 1 would be more appropriate.
self._timeout = round(max(self._when - time, 0) * 1000)
QAsyncioHandle._start(self)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
from . import events
+from typing import Any, Callable
+
import asyncio
import contextvars
import enum
-import typing
class QAsyncioFuture():
DONE_WITH_RESULT = enum.auto()
DONE_WITH_EXCEPTION = enum.auto()
- def __init__(self, *, loop: typing.Optional["events.QAsyncioEventLoop"] = None,
- context: typing.Optional[contextvars.Context] = None) -> None:
+ def __init__(self, *, loop: "events.QAsyncioEventLoop | None" = None,
+ context: contextvars.Context | None = None) -> None:
self._loop: "events.QAsyncioEventLoop"
if loop is None:
self._loop = asyncio.events.get_event_loop() # type: ignore[assignment]
self._context = context
self._state = QAsyncioFuture.FutureState.PENDING
- self._result: typing.Any = None
- self._exception: typing.Optional[BaseException] = None
+ self._result: Any = None
+ self._exception: BaseException | None = None
- self._cancel_message: typing.Optional[str] = None
+ self._cancel_message: str | None = None
# List of callbacks that are called when the future is done.
- self._callbacks: typing.List[typing.Callable] = list()
+ self._callbacks: list[Callable] = list()
def __await__(self):
if not self.done():
__iter__ = __await__
- def _schedule_callbacks(self, context: typing.Optional[contextvars.Context] = None):
+ def _schedule_callbacks(self, context: contextvars.Context | None = None):
""" A future can optionally have callbacks that are called when the future is done. """
for cb in self._callbacks:
self._loop.call_soon(
cb, self, context=context if context else self._context)
- def result(self) -> typing.Union[typing.Any, Exception]:
+ def result(self) -> Any | Exception:
if self._state == QAsyncioFuture.FutureState.DONE_WITH_RESULT:
return self._result
if self._state == QAsyncioFuture.FutureState.DONE_WITH_EXCEPTION and self._exception:
raise asyncio.CancelledError
raise asyncio.InvalidStateError
- def set_result(self, result: typing.Any) -> None:
+ def set_result(self, result: Any) -> None:
self._result = result
self._state = QAsyncioFuture.FutureState.DONE_WITH_RESULT
self._schedule_callbacks()
def cancelled(self) -> bool:
return self._state == QAsyncioFuture.FutureState.CANCELLED
- def add_done_callback(self, cb: typing.Callable, *,
- context: typing.Optional[contextvars.Context] = None) -> None:
+ def add_done_callback(self, cb: Callable, *,
+ context: contextvars.Context | None = None) -> None:
if self.done():
self._loop.call_soon(
cb, self, context=context if context else self._context)
else:
self._callbacks.append(cb)
- def remove_done_callback(self, cb: typing.Callable) -> int:
+ def remove_done_callback(self, cb: Callable) -> int:
original_len = len(self._callbacks)
self._callbacks = [_cb for _cb in self._callbacks if _cb != cb]
return original_len - len(self._callbacks)
- def cancel(self, msg: typing.Optional[str] = None) -> bool:
+ def cancel(self, msg: str | None = None) -> bool:
if self.done():
return False
self._state = QAsyncioFuture.FutureState.CANCELLED
self._schedule_callbacks()
return True
- def exception(self) -> typing.Optional[BaseException]:
+ def exception(self) -> BaseException | None:
if self._state == QAsyncioFuture.FutureState.CANCELLED:
raise asyncio.CancelledError
if self.done():
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
from . import events
from . import futures
+from typing import Any
+
import asyncio
import collections.abc
import concurrent.futures
import contextvars
-import typing
class QAsyncioTask(futures.QAsyncioFuture):
""" https://docs.python.org/3/library/asyncio-task.html """
- def __init__(self, coro: typing.Union[collections.abc.Generator, collections.abc.Coroutine], *,
- loop: typing.Optional["events.QAsyncioEventLoop"] = None,
- name: typing.Optional[str] = None,
- context: typing.Optional[contextvars.Context] = None) -> None:
+ def __init__(self, coro: collections.abc.Generator | collections.abc.Coroutine, *,
+ loop: "events.QAsyncioEventLoop | None" = None, name: str | None = None,
+ context: contextvars.Context | None = None) -> None:
super().__init__(loop=loop, context=context)
self._coro = coro # The coroutine for which this task was created.
# The task creates a handle for its coroutine. The handle enqueues the
# task's step function as its callback in the event loop.
- self._handle = self._loop.call_soon(self._step, context=self._context)
+ self._loop.call_soon(self._step, context=self._context)
# The task step function executes the coroutine until it finishes,
# raises an exception or returns a future. If a future was returned,
- # the task will await its completion (or exception).
- self._future_to_await: typing.Optional[asyncio.Future] = None
+ # the task will await its completion (or exception). If the task is
+ # cancelled while it awaits a future, this future must also be
+ # cancelled in order for the cancellation to be successful.
+ self._future_to_await: asyncio.Future | None = None
- self._cancelled = False
- self._cancel_message: typing.Optional[str] = None
+ self._cancelled = False # PYSIDE-2644; see _step
+ self._cancel_count = 0
+ self._cancel_message: str | None = None
# https://docs.python.org/3/library/asyncio-extending.html#task-lifetime-support
asyncio._register_task(self) # type: ignore[arg-type]
class QtTaskApiMisuseError(Exception):
pass
- def set_result(self, result: typing.Any) -> None: # type: ignore[override]
+ def set_result(self, result: Any) -> None: # type: ignore[override]
# This function is not inherited from the Future APIs.
raise QAsyncioTask.QtTaskApiMisuseError("Tasks cannot set results")
- def set_exception(self, exception: typing.Any) -> None: # type: ignore[override]
+ def set_exception(self, exception: Any) -> None: # type: ignore[override]
# This function is not inherited from the Future APIs.
raise QAsyncioTask.QtTaskApiMisuseError("Tasks cannot set exceptions")
def _step(self,
- exception_or_future: typing.Union[
- BaseException, futures.QAsyncioFuture, None] = None) -> None:
+ exception_or_future: BaseException | futures.QAsyncioFuture | None = None) -> None:
"""
The step function is the heart of a task. It is scheduled in the event
loop repeatedly, executing the coroutine "step" by "step" (i.e.,
result = None
self._future_to_await = None
+ if self._cancelled:
+ exception_or_future = asyncio.CancelledError(self._cancel_message)
+ self._cancelled = False
+
if asyncio.futures.isfuture(exception_or_future):
try:
exception_or_future.result()
# called again.
result.add_done_callback(
self._step, context=self._context) # type: ignore[arg-type]
+
+ # The task will await the completion (or exception) of this
+ # future. If the task is cancelled while it awaits a future,
+ # this future must also be cancelled.
self._future_to_await = result
+
if self._cancelled:
- # If the task was cancelled, then a new future should be
- # cancelled as well. Otherwise, in some scenarios like
- # a loop inside the task and with bad timing, if the new
+ # PYSIDE-2644: If the task was cancelled at this step and a
+ # new future was created to be awaited, then it should be
+ # cancelled as well. Otherwise, in some scenarios like a
+ # loop inside the task and with bad timing, if the new
# future is not cancelled, the task would continue running
# in this loop despite having been cancelled. This bad
# timing can occur especially if the first future finishes
asyncio._leave_task(self._loop, self) # type: ignore[arg-type]
if self._exception:
+ message = str(self._exception)
+ if message == "None":
+ message = ""
+ else:
+ message = "An exception occurred during task execution"
self._loop.call_exception_handler({
- "message": (str(self._exception) if self._exception
- else "An exception occurred during task "
- "execution"),
+ "message": message,
"exception": self._exception,
"task": self,
"future": (exception_or_future
# https://docs.python.org/3/library/asyncio-extending.html#task-lifetime-support
asyncio._unregister_task(self) # type: ignore[arg-type]
- def get_stack(self, *, limit=None) -> typing.List[typing.Any]:
+ def get_stack(self, *, limit=None) -> list[Any]:
# TODO
raise NotImplementedError("QtTask.get_stack is not implemented")
# TODO
raise NotImplementedError("QtTask.print_stack is not implemented")
- def get_coro(self) -> typing.Union[collections.abc.Generator, collections.abc.Coroutine]:
+ def get_coro(self) -> collections.abc.Generator | collections.abc.Coroutine:
return self._coro
def get_name(self) -> str:
def set_name(self, value) -> None:
self._name = str(value)
- def cancel(self, msg: typing.Optional[str] = None) -> bool:
+ def cancel(self, msg: str | None = None) -> bool:
if self.done():
return False
+ self._cancel_count += 1
self._cancel_message = msg
- self._handle.cancel()
if self._future_to_await is not None:
# A task that is awaiting a future must also cancel this future in
# order for the cancellation to be successful.
self._future_to_await.cancel(msg)
- self._cancelled = True
+ self._cancelled = True # PYSIDE-2644; see _step
return True
- def uncancel(self) -> None:
- # TODO
- raise NotImplementedError("QtTask.uncancel is not implemented")
+ def uncancel(self) -> int:
+ if self._cancel_count > 0:
+ self._cancel_count -= 1
+ return self._cancel_count
- def cancelling(self) -> bool:
- # TODO
- raise NotImplementedError("QtTask.cancelling is not implemented")
+ def cancelling(self) -> int:
+ return self._cancel_count
${QtCore_GEN_DIR}/qdeadlinetimer_wrapper.cpp
${QtCore_GEN_DIR}/qdir_wrapper.cpp
${QtCore_GEN_DIR}/qdiriterator_wrapper.cpp
+${QtCore_GEN_DIR}/qdirlisting_wrapper.cpp
+${QtCore_GEN_DIR}/qdirlisting_direntry_wrapper.cpp
${QtCore_GEN_DIR}/qdynamicpropertychangeevent_wrapper.cpp
${QtCore_GEN_DIR}/qeasingcurve_wrapper.cpp
${QtCore_GEN_DIR}/qelapsedtimer_wrapper.cpp
${QtCore_GEN_DIR}/qmimetype_wrapper.cpp
${QtCore_GEN_DIR}/qmodelindex_wrapper.cpp
${QtCore_GEN_DIR}/qmodelroledata_wrapper.cpp
+${QtCore_GEN_DIR}/qmodelroledataspan_wrapper.cpp
${QtCore_GEN_DIR}/qmutex_wrapper.cpp
${QtCore_GEN_DIR}/qnativeipckey_wrapper.cpp
${QtCore_GEN_DIR}/qobject_wrapper.cpp
${QtCore_GEN_DIR}/qsysinfo_wrapper.cpp
${QtCore_GEN_DIR}/qsystemsemaphore_wrapper.cpp
${QtCore_GEN_DIR}/qt_wrapper.cpp
+${QtCore_GEN_DIR}/qtcorehelper_qdirlistingiterator_wrapper.cpp
${QtCore_GEN_DIR}/qtcorehelper_qgenericargumentholder_wrapper.cpp
${QtCore_GEN_DIR}/qtcorehelper_qgenericreturnargumentholder_wrapper.cpp
${QtCore_GEN_DIR}/qtcorehelper_qiopipe_wrapper.cpp
// that has added any python fields or slots to its object layout.
// See https://mail.python.org/pipermail/python-list/2009-January/520733.html
if (type->tp_bases) {
- for (Py_ssize_t i = 0, size = PyTuple_GET_SIZE(type->tp_bases); i < size; ++i) {
- auto baseType = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(type->tp_bases, i));
+ for (Py_ssize_t i = 0, size = PyTuple_Size(type->tp_bases); i < size; ++i) {
+ auto baseType = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(type->tp_bases, i));
const QMetaType derived = QVariant_resolveMetaType(baseType);
if (derived.isValid())
return derived;
}
Shiboken::AutoDecRef fast(PySequence_Fast(list, "Failed to convert QVariantList"));
- const Py_ssize_t size = PySequence_Fast_GET_SIZE(fast.object());
+ const Py_ssize_t size = PySequence_Size(fast.object());
for (Py_ssize_t i = 0; i < size; ++i) {
- PyObject *item = PySequence_Fast_GET_ITEM(fast.object(), i);
+ Shiboken::AutoDecRef item(PySequence_GetItem(fast.object(), i));
if (PyUnicode_Check(item) == 0)
return false;
}
QString qObjectTr(PyTypeObject *type, const char *sourceText, const char *disambiguation, int n)
{
PyObject *mro = type->tp_mro;
- auto len = PyTuple_GET_SIZE(mro);
+ auto len = PyTuple_Size(mro);
QString result = QString::fromUtf8(sourceText);
QString oldResult = result;
static auto *sbkObjectType = reinterpret_cast<PyTypeObject *>(SbkObject_TypeF());
for (Py_ssize_t idx = 0; idx < len - 1; ++idx) {
// Skip the last class which is `object`.
- auto *type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
+ auto *type = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
if (type == sbkObjectType)
continue;
const char *context = type->tp_name;
CustomFunctions<MAX_CUSTOM_FUNCTIONS-1>::init();
}
-QEasingCurve::EasingFunction PySideEasingCurveFunctor::createCustomFuntion(PyObject *parent, PyObject *pyFunc)
+QEasingCurve::EasingFunction PySideEasingCurveFunctor::createCustomFunction(PyObject *parent, PyObject *pyFunc)
{
for(int i=0; i < MAX_CUSTOM_FUNCTIONS; i++) {
CustomFunctionsData &data = CustomFunctionsData::m_list[i];
PyObject_SetAttr(m_parent, Shiboken::PyMagicName::ecf(), m_func);
PySide::WeakRef::create(m_parent, deleteData, this);
}
-
{
public:
static void init();
- static QEasingCurve::EasingFunction createCustomFuntion(PyObject *parent, PyObject *pyFunc);
+ static QEasingCurve::EasingFunction createCustomFunction(PyObject *parent, PyObject *pyFunc);
qreal operator()(qreal progress);
return d->m_argument.data();
}
+// QDirListing::const_iterator has no copy semantics (shared internal state, QTBUG-125512).
+// The Python iterable semantics (calling __next__() first before retrieving the first value)
+// can therefore not be implemented as "return iterator++;". Wrap a helper class
+// around it that does a no-op in the first call to __next__().
+struct QDirListingIteratorPrivate
+{
+ enum State { First, Iterating, End };
+
+ explicit QDirListingIteratorPrivate(const QDirListing &dl) :
+ iterator(dl.cbegin()), state(First) {}
+ QDirListingIteratorPrivate() : state(End) {}
+
+ bool next();
+
+ QDirListing::const_iterator iterator;
+ State state;
+};
+
+inline bool QDirListingIteratorPrivate::next()
+{
+ switch (state) {
+ case First:
+ state = iterator != QDirListing::sentinel{} ? Iterating : End;
+ break;
+ case Iterating:
+ if (++iterator == QDirListing::sentinel{})
+ state = End;
+ break;
+ case End:
+ break;
+ }
+ return state != End;
+}
+
+QDirListingIterator::QDirListingIterator(const QDirListing &dl) :
+ d(std::make_shared<QDirListingIteratorPrivate>(dl))
+{
+}
+
+QDirListingIterator::QDirListingIterator() :
+ d(std::make_shared<QDirListingIteratorPrivate>())
+{
+}
+
+QDirListingIterator::QDirListingIterator(const QDirListingIterator &) = default;
+QDirListingIterator &QDirListingIterator::operator=(const QDirListingIterator &) = default;
+QDirListingIterator::QDirListingIterator(QDirListingIterator &&) noexcept = default;
+QDirListingIterator &QDirListingIterator::operator=(QDirListingIterator &&) noexcept = default;
+QDirListingIterator::~QDirListingIterator() = default;
+
+bool QDirListingIterator::next()
+{
+ return d->next();
+}
+
+const QDirListing::DirEntry &QDirListingIterator::value() const
+{
+ Q_ASSERT(d->state == QDirListingIteratorPrivate::Iterating);
+ return *d->iterator;
+}
+
+bool QDirListingIterator::atEnd() const
+{
+ return d->state == QDirListingIteratorPrivate::End;
+}
+
} // namespace QtCoreHelper
QT_END_NAMESPACE
<inject-code class="native" position="beginning" file="../glue/qtcore.cpp"
snippet="darwin_permission_plugin"/>
- <add-function signature="qDebug(const char*)">
- <inject-code file="../glue/qtcore.cpp" snippet="use-stream-for-format-security"/>
+ <add-function signature="qDebug(const char*@message@)">
+ <inject-code file="../glue/qtcore.cpp" snippet="qdebug-format-string"/>
<modify-argument index="1" pyi-type="str"/>
</add-function>
- <add-function signature="qCritical(const char*)">
- <inject-code file="../glue/qtcore.cpp" snippet="use-stream-for-format-security"/>
+ <add-function signature="qCritical(const char*@message@)">
+ <inject-code file="../glue/qtcore.cpp" snippet="qdebug-format-string"/>
<modify-argument index="1" pyi-type="str"/>
</add-function>
- <add-function signature="qFatal(const char*)">
+ <add-function signature="qFatal(const char*@message@)">
<inject-code file="../glue/qtcore.cpp" snippet="qfatal"/>
<modify-argument index="1" pyi-type="str"/>
</add-function>
- <add-function signature="qWarning(const char*)">
- <inject-code file="../glue/qtcore.cpp" snippet="use-stream-for-format-security"/>
+ <add-function signature="qInfo(const char*@message@)">
+ <inject-code file="../glue/qtcore.cpp" snippet="qdebug-format-string"/>
+ <modify-argument index="1" pyi-type="str"/>
+ </add-function>
+ <add-function signature="qWarning(const char*@message@)">
+ <inject-code file="../glue/qtcore.cpp" snippet="qdebug-format-string"/>
<modify-argument index="1" pyi-type="str"/>
</add-function>
<add-function signature="Q_ARG(PyObject *@type@, PyObject *@value@)"
return-type="QtCoreHelper::QGenericArgumentHolder">
<inject-code file="../glue/qtcore.cpp" snippet="q_arg"/>
- <inject-documentation format="target" mode="append">
- This function takes a type (or a type string) and a value of that type
- and returns an internal object that can be passed to
- QMetaObject.invokeMethod(). See also Q_RETURN_ARG().
- </inject-documentation>
+ <inject-documentation format="target" mode="append"
+ file="../doc/qtcore.rst" snippet="q_arg"/>
</add-function>
<add-function signature="Q_RETURN_ARG(PyObject *@type@)"
return-type="QtCoreHelper::QGenericReturnArgumentHolder">
<inject-code file="../glue/qtcore.cpp" snippet="q_return_arg"/>
- <inject-documentation format="target" mode="append">
- This macro takes a type (or a type string) a value of which is then
- returned by QMetaObject::invokeMethod(). See also Q_ARG().
- </inject-documentation>
+ <inject-documentation format="target" mode="append"
+ file="../doc/qtcore.rst" snippet="q_return_arg"/>
</add-function>
<!-- TODO: We do not support void* or const void* as arg -->
</extra-includes>
<enum-type name="Type" python-type="IntEnum"/>
<enum-type name="TypeFlag" flags="TypeFlags"/>
- <add-function signature="QMetaType(PyTypeObject*)">
+ <add-function signature="QMetaType(PyTypeObject*@py_type@)">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
snippet="metatype-from-type"/>
</add-function>
<!-- PYSIDE-1735: The class QMetaType.Type must be allowed because int is gone -->
- <add-function signature="QMetaType(QMetaType::Type)">
+ <add-function signature="QMetaType(QMetaType::Type@type@)">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
snippet="metatype-from-metatype-type"/>
</add-function>
<enum-type name="ClipOperation"/>
<enum-type name="ConnectionType"/>
<enum-type name="ContextMenuPolicy"/>
+ <enum-type name="ContextMenuTrigger" since="6.8"/>
<enum-type name="CoordinateSystem" since="4.6"/>
<enum-type name="Corner"/>
<enum-type name="CursorShape"/>
<enum-type name="TextFormat"/>
<enum-type name="TextInteractionFlag" flags="TextInteractionFlags"/>
<enum-type name="TileRule" since="4.6"/>
+ <enum-type name="TimerId" since="6.8"/>
<enum-type name="TimerType"/>
<enum-type name="TimeSpec"/>
<enum-type name="ToolBarArea" flags="ToolBarAreas"/>
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qt-qabs"/>
</add-function>
- <add-function signature="qAddPostRoutine(PyObject*)">
+ <add-function signature="qAddPostRoutine(PyObject*@callable@)">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qt-addpostroutine"/>
</add-function>
<inject-code class="target" position="end" file="../glue/qtcore.cpp" snippet="qt-qaddpostroutine"/>
<inject-code class="native" position="beginning" file="../glue/qtcore.cpp" snippet="qt-messagehandler"/>
<inject-code class="native" position="beginning" file="../glue/qtcore.cpp"
snippet="qlatin1string-check"/>
- <add-function signature="qInstallMessageHandler(PyObject)" return-type="PyObject">
+ <add-function signature="qInstallMessageHandler(PyObject@callable@)" return-type="PyObject">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qt-installmessagehandler"/>
</add-function>
<extra-includes>
<include file-name="pysideqhash.h" location="global"/>
</extra-includes>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="%i, %i, %i, %i"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="iiii"/>
</value-type>
<value-type name="QLineF">
<enum-type name="IntersectionType"/>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="%f, %f, %f, %f"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="dddd"/>
<object-type name="QResource">
<enum-type name="Compression"/>
<modify-function signature="data()const">
- <inject-documentation format="target">
+ <inject-documentation format="target" emphasis="language-note">
Returns a read only buffer object pointing to the segment of data that this resource represents. If the resource is compressed the data returns is compressed and qUncompress() must be used to access the data. If the resource is a directory None is returned.
</inject-documentation>
<modify-argument index="return">
<extra-includes>
<include file-name="datetime.h" location="global"/>
</extra-includes>
- <add-function signature="__repr__" return-type="PyObject">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="%i, %i, %i"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="iii"/>
<include file-name="datetime.h" location="global"/>
</extra-includes>
<!-- Somewhat internal constructor used to pickle QDateTime -->
- <add-function signature="QDateTime(int,int,int,int,int,int,int,int=Qt::LocalTime)">
+ <add-function signature="QDateTime(int@year@,int@month@,int@day@,int@h@,int@m@,int@s@,int@ms@,int@spec@=Qt::LocalTime)">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qdatetime-1"/>
</add-function>
- <add-function signature="QDateTime(int,int,int,int,int,int)">
+ <add-function signature="QDateTime(int@year@,int@month@,int@day@,int@h@,int@m@,int@s@)">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qdatetime-2"/>
</add-function>
<!-- PYSIDE-1735: Qt::TimeSpec is no more compatible with int -->
- <add-function signature="QDateTime(int,int,int,int,int,int,int,Qt::TimeSpec=Qt::LocalTime)">
+ <add-function signature="QDateTime(int@year@,int@month@,int@day@,int@h@,int@m@,int@s@,int@ms@,Qt::TimeSpec@spec@=Qt::LocalTime)">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qdatetime-3"/>
</add-function>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="%i, %i, %i, %i, %i, %i, %i, %i"/>
</insert-template>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="iiiiiiii"/>
</modify-function>
<!-- PYSIDE-1499: End of insertion -->
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="s"/>
</add-function>
</value-type>
+ <object-type name="QDirListing" since="6.8">
+ <extra-includes>
+ <include file-name="qtcorehelper.h" location="local"/>
+ </extra-includes>
+ <enum-type name="IteratorFlag" flags="IteratorFlags"/>
+ <value-type name="DirEntry">
+ <add-function signature="__repr__" return-type="PyObject*">
+ <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
+ snippet="qdirlisting-direntry-repr"/>
+ </add-function>
+ </value-type>
+ <add-function signature="__iter__()" return-type="PyObject*">
+ <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
+ snippet="qdirlisting-iter"/>
+ </add-function>
+ </object-type>
+
<value-type name="QPermission" since="6.5">
<configuration condition="QT_CONFIG(permissions)"/>
</value-type>
</value-type>
<value-type name="QPoint">
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="%i, %i"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="ii"/>
<!--### -->
</value-type>
<value-type name="QPointF">
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="%f, %f"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="dd"/>
<!--### -->
</value-type>
<value-type name="QRect">
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="%i, %i, %i, %i"/>
</insert-template>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="iiii"/>
</modify-function>
</value-type>
<value-type name="QRectF">
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="%f, %f, %f, %f"/>
FIXME These functions return qreal. Will convert to double (format
string) mess things up in other architectures?
-->
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="dddd"/>
</modify-function>
</value-type>
<value-type name="QSize">
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="%i, %i"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="ii"/>
<!--### -->
</value-type>
<value-type name="QSizeF">
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="%f, %f"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="dd"/>
<include file-name="datetime.h" location="global"/>
</extra-includes>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="%i, %i, %i, %i"/>
</insert-template>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="iiii"/>
<enum-type name="StringFormat"/>
<enum-type name="Variant"/>
<enum-type name="Version"/>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="'%s'"/>
</insert-template>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="s"/>
<add-function signature="system()" return-type="QLocale" static="yes">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
snippet="qlocale_system"/>
- <inject-documentation mode="append" format="target">
- Returns a QLocale object initialized to the system locale.
-
- The system locale may use system-specific sources for locale data, where available,
- otherwise falling back on QLocale's built-in database entry for the language, script and
- territory the system reports.
-
- For example, on Windows, this locale will use the decimal/grouping characters and
- date/time formats specified in the system configuration panel.
-
- .. note:: Qt for Python on macOS will not reflect the user's region and language
- preferences though QLocale::system(), but will instead reflect the environment
- variables POSIX uses to specify locale, similar to Python's locale module. If the
- system locale cannot be determined, which can be due to none of the variables 'LC_ALL',
- 'LC_CTYPE', 'LANG' or 'LANGUAGE' being set by your environment, then the default POSIX
- locale or 'C' locale is returned.
-
- See also c().
- </inject-documentation>
+ <inject-documentation mode="append" format="target"
+ file="../doc/qtcore.rst" snippet="qlocale-system"/>
</add-function>
<modify-function signature="toTime(QString,QLocale::FormatType)const">
<modify-argument index="2">
<modify-function signature="^to.*\(.*QStringView.*$" remove="all"/>
</value-type>
<value-type name="QBitArray">
- <add-function signature="__len__">
+ <add-function signature="__len__" return-type="int">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qbitarray-len"/>
</add-function>
<add-function signature="__getitem__">
<modify-function signature="exit(int)" allow-thread="yes"/>
</object-type>
- <value-type name="QModelRoleData"/>
+ <value-type name="QModelRoleData">
+ <add-function signature="setData(const QVariant &@data@)">
+ <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
+ snippet="qmodelroledata-setdata"/>
+ </add-function>
+ </value-type>
+
+ <value-type name="QModelRoleDataSpan">
+ <modify-function signature="dataForRole(int)const" remove="all"/>
+ <add-function signature="__len__" return-type="int">
+ <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
+ snippet="qmodelroledataspan-len"/>
+ </add-function>
+ <add-function signature="__getitem__">
+ <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
+ snippet="qmodelroledataspan-getitem"/>
+ </add-function>
+ </value-type>
- <object-type name="QAbstractItemModel">
+ <!-- Register meta type for QML properties -->
+ <object-type name="QAbstractItemModel" qt-register-metatype="yes">
<enum-type name="CheckIndexOption" flags="CheckIndexOptions"/>
<enum-type name="LayoutChangeHint"/>
<!-- This function was replaced by a added function -->
</modify-function>
<add-function signature="createIndex(int@row@,int@column@,PyObject*@ptr@)const" return-type="QModelIndex">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qabstractitemmodel-createindex"/>
- <inject-documentation mode="append" format="target">
- Creates a model index for the given row and column with the internal pointer ptr.
- When using a QSortFilterProxyModel, its indexes have their own internal pointer.
- It is not advisable to access this internal pointer outside of the model.
- Use the data() function instead.
- This function provides a consistent interface that model subclasses must use to create model indexes.
-
- .. warning:: Because of some Qt/Python itegration rules, the ptr argument do not get the reference
- incremented during the QModelIndex life time. So it is necessary to keep the object used
- on ptr argument alive during the whole process.
- Do not destroy the object if you are not sure about that.
- </inject-documentation>
+ <inject-documentation mode="append" format="target"
+ file="../doc/qtcore.rst" snippet="qabstractitemmodel-createindex"/>
</add-function>
<inject-code class="target" position="end" file="../glue/qtcore.cpp" snippet="qabstractitemmodel"/>
<modify-function signature="mimeData(QList<QModelIndex>)const">
<reference-count action="set"/>
</modify-argument>
</modify-function>
- <!-- FIXME PYSIDE 7: Remove this (QT6_DECL_NEW_OVERLOAD_TAIL) -->
- <modify-function signature="^moveToThread\(.*\)" remove="all"/>
- <declare-function signature="moveToThread(QThread*)" return-type="bool"/>
<modify-function signature="deleteLater()">
<modify-argument index="this">
<define-ownership owner="c++"/>
</modify-function>
<!-- End of Invalidate-after-use fix -->
<modify-function signature="parent()const">
- <modify-argument index="this">
- <parent index="return" action="add"/>
- </modify-argument>
<modify-argument index="return">
<define-ownership class="target" owner="default"/>
</modify-argument>
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-6"/>
</add-function>
- <add-function signature="emit(const char*,...)" return-type="bool">
+ <add-function signature="emit(const char*@signal@,...)" return-type="bool">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-emit"/>
</add-function>
<add-function signature="disconnect(const char*@signal@,PyCallable*@functor@)" return-type="bool">
<add-function signature="findChild(PyTypeObject*@type@,const QString&@name@={},Qt::FindChildOptions@options@=Qt::FindChildrenRecursively)"
return-type="PyObject*">
- <inject-documentation format="target" mode="append">
- To find the child of a certain QObject, the first argument of this function should be the child's type, and the second the name of the child:
-
- ::
-
- ...
- parent = QWidget()
- ...
- # The first argument must be the child type
- child1 = parent.findChild(QPushButton, "child_button")
- child2 = parent.findChild(QWidget, "child_widget")
-
- </inject-documentation>
+ <inject-documentation format="target" mode="append"
+ file="../doc/qtcore.rst" snippet="qobject-findChild"/>
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-findchild-2"/>
- <modify-argument index="return">
+ <modify-argument index="return" pyi-type="Optional[PlaceHolderType]">
<parent index="this" action="add"/>
</modify-argument>
</add-function>
Like the method *findChild*, the first parameter should be the child's type.
</inject-documentation>
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-findchildren"/>
- <modify-argument index="return">
+ <modify-argument index="return" pyi-type="Iterable[PlaceHolderType]">
<parent index="this" action="add"/>
</modify-argument>
</add-function>
<add-function signature="findChildren(PyTypeObject*@type@,const QRegularExpression&@pattern@,Qt::FindChildOptions@options@=Qt::FindChildrenRecursively)"
return-type="PySequence*" >
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-findchildren"/>
- <modify-argument index="return">
+ <modify-argument index="return" pyi-type="Iterable[PlaceHolderType]">
<parent index="this" action="add"/>
</modify-argument>
</add-function>
<extra-includes>
<include file-name="QStringList" location="global"/>
</extra-includes>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="'%s'"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="s"/>
</modify-function>
<!-- PYSIDE-1499: End of insertion -->
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="s"/>
<modify-function signature="operator+(QByteArray,const char*)" remove="all"/>
<modify-function signature="operator+(const char*,QByteArray)" remove="all"/>
<modify-function signature="operator+(QByteArray,const char*)" remove="all"/>
- <add-function signature="operator+(PyBytes,QByteArray)">
+ <add-function signature="operator+(PyBytes,QByteArray)" return-type="QByteArray">
<inject-code file="../glue/qtcore.cpp" snippet="qbytearray-operatorplus-1"/>
</add-function>
<add-function signature="operator+(PyByteArray, QByteArray)" return-type="QByteArray">
<add-function signature="operator+=(PyByteArray)" return-type="QByteArray">
<inject-code file="../glue/qtcore.cpp" snippet="qbytearray-operatorplusequal"/>
</add-function>
- <add-function signature="operator==(PyUnicode)">
+ <add-function signature="operator==(PyUnicode)" return-type="bool">
<inject-code file="../glue/qtcore.cpp" snippet="qbytearray-operatorequalequal"/>
</add-function>
- <add-function signature="operator!=(PyUnicode)">
+ <add-function signature="operator!=(PyUnicode)" return-type="bool">
<inject-code file="../glue/qtcore.cpp" snippet="qbytearray-operatornotequal"/>
</add-function>
- <add-function signature="operator>(PyUnicode)">
+ <add-function signature="operator>(PyUnicode)" return-type="bool">
<inject-code file="../glue/qtcore.cpp" snippet="qbytearray-operatorgreater"/>
</add-function>
- <add-function signature="operator>=(PyUnicode)">
+ <add-function signature="operator>=(PyUnicode)" return-type="bool">
<inject-code file="../glue/qtcore.cpp" snippet="qbytearray-operatorgreaterequal"/>
</add-function>
- <add-function signature="operator<(PyUnicode)">
+ <add-function signature="operator<(PyUnicode)" return-type="bool">
<inject-code file="../glue/qtcore.cpp" snippet="qbytearray-operatorlower"/>
</add-function>
- <add-function signature="operator<=(PyUnicode)">
+ <add-function signature="operator<=(PyUnicode)" return-type="bool">
<inject-code file="../glue/qtcore.cpp" snippet="qbytearray-operatorlowerequal"/>
</add-function>
<!-- ### -->
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qbytearray-repr"/>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="N"/>
</insert-template>
</inject-code>
</add-function>
- <add-function signature="QByteArray(PyByteArray)">
+ <add-function signature="QByteArray(PyByteArray@data@)">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qbytearray-2"/>
</add-function>
- <add-function signature="QByteArray(PyBytes)">
+ <add-function signature="QByteArray(PyBytes@data@)">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qbytearray-3"/>
</add-function>
<!-- buffer protocol -->
<insert-template name="fix_bool*"/>
</inject-code>
</modify-function>
- <add-function signature="__str__" return-type="PyObject*">
+ <add-function signature="__str__" return-type="str">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qbytearray-str"/>
</add-function>
- <add-function signature="__len__">
+ <add-function signature="__len__" return-type="int">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qbytearray-len"/>
</add-function>
<add-function signature="__getitem__">
<add-function signature="__msetitem__">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qbytearray-msetitem"/>
</add-function>
+ <modify-function signature="fromRawData(const char*, qsizetype)">
+ <modify-argument index="1" pyi-type="str"/>
+ </modify-function>
</value-type>
<primitive-type name="QByteArrayView" view-on="QByteArray">
<conversion-rule>
<object-type name="QIOPipe"/>
<value-type name="QGenericArgumentHolder"/>
<value-type name="QGenericReturnArgumentHolder"/>
+ <value-type name="QDirListingIterator">
+ <add-function signature="__iter__()" return-type="PyObject*">
+ <inject-code class="target" position="beginning">
+ <insert-template name="__iter__"/>
+ </inject-code>
+ </add-function>
+ <add-function signature="__next__()" return-type="PyObject*">
+ <inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
+ snippet="qdirlistingiterator-next"/>
+ </add-function>
+ </value-type>
</namespace-type>
<!-- Qt5 addition -->
<configuration condition="QT_CONFIG(thread)"/>
<modify-function signature="acquire(int)" allow-thread="yes"/>
<modify-function signature="tryAcquire(int,int)" allow-thread="yes"/>
+ <!-- PYSIDE-2845, clashes with snake-case -->
+ <modify-function signature="try_acquire()" remove="yes"/>
</object-type>
<object-type name="QSemaphoreReleaser">
<configuration condition="QT_CONFIG(thread)"/>
<object-type name="QTimer">
<extra-includes>
<include file-name="pysidestaticstrings.h" location="global"/>
+ <include file-name="qobjectconnect.h" location="global"/>
</extra-includes>
<inject-code class="native" position="beginning" file="../glue/qtcore.cpp"
snippet="qtimer-singleshot-functorclass"/>
</extra-includes>
<add-function signature="QCoreApplication(QStringList@args@)">
<inject-code file="../glue/qtcore.cpp" snippet="qcoreapplication-1"/>
- <inject-documentation format="target" mode="append">
- Constructs a Qt kernel application. Kernel applications are applications
- without a graphical user interface. These type of applications are used
- at the console or as server processes.
-
- The *args* argument is processed by the application, and made available
- in a more convenient form by the :meth:`~PySide6.QtCore.QCoreApplication.arguments()`
- method.
- </inject-documentation>
+ <inject-documentation format="target" mode="append"
+ file="../doc/qtcore.rst" snippet="qcoreapplication-init"/>
</add-function>
<add-function signature="QCoreApplication()">
<inject-code file="../glue/qtcore.cpp" snippet="qcoreapplication-2"/>
<modify-function signature="value(QAnyStringView,const QVariant&)const" remove="all"/>
<add-function signature="value(const QString&, const QVariant& @defaultValue@ = {}, PyObject* @type@ = nullptr)" return-type="PyObject*">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qsettings-value"/>
- <inject-documentation mode="append" format="target">
- Custom overload that adds an optional named parameter to the function ``value()``
- to automatically cast the type that is being returned by the function.
-
- An example of this situation could be an ini file that contains
- the value of a one-element list::
-
- settings.setValue('var', ['a'])
-
- The the ini file will be::
-
- [General]
- var=a # we cannot know that this is a list!
-
- Once we read it, we could specify if we want
- the default behavior, a str, or to cast the output
- to a list.
-
- settings.value('var') # Will get "a"
- settings.value('var', type=list) # Will get ["a"]
- </inject-documentation>
+ <inject-documentation mode="append" format="target"
+ file="../doc/qtcore.rst" snippet="qsettings-value"/>
</add-function>
</object-type>
<object-type name="QEvent" polymorphic-id-expression="%1->type() == QEvent::None"
qt-register-metatype="base">
<enum-type name="Type" python-type="IntEnum"/>
- <add-function signature="__repr__" return-type="PyObject">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning"
file="../glue/qtcore.cpp" snippet="repr-qevent"/>
</add-function>
<!-- ### Replaced by write<TYPE> methods -->
<modify-function signature="operator>>(qint8&)" remove="all"/>
- <modify-function signature="operator>>(bool&)" remove="all"/>
<modify-function signature="operator>>(quint8&)" remove="all"/>
<modify-function signature="operator>>(qint16&)" remove="all"/>
<modify-function signature="operator>>(quint16&)" remove="all"/>
<!-- ### Replaced by read<TYPE> methods -->
<modify-function signature="operator<<(qint8)" remove="all"/>
- <modify-function signature="operator<<(bool)" remove="all"/>
<modify-function signature="operator<<(quint8)" remove="all"/>
<modify-function signature="operator<<(qint16)" remove="all"/>
<modify-function signature="operator<<(quint16)" remove="all"/>
<modify-function signature="reserveThread()" allow-thread="yes"/>
<modify-function signature="setMaxThreadCount(int)" allow-thread="yes"/>
<modify-function signature="waitForDone(int)" allow-thread="yes"/>
+ <modify-function signature="waitForDone(QDeadlineTimer)" allow-thread="yes"/>
<modify-function signature="start(QRunnable*,int)" allow-thread="yes">
<modify-argument index="1">
<parent index="this" action="add"/>
<parent index="this" action="add"/>
</modify-argument>
</modify-function>
- <add-function signature="tryStart(PyCallable)" return-type="bool">
+ <add-function signature="tryStart(PyCallable@callable@)" return-type="bool">
<inject-code class="target" position="beginning"
file="../glue/qtcore.cpp"
snippet="std-function-void-lambda"/>
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp"
snippet="qmetaobject-invokemethod-return-arg"/>
</add-function>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qmetaobject-repr"/>
</add-function>
<modify-function signature="indexOfClassInfo(const char *)">
</extra-includes>
<inject-code file="../glue/qtcore.cpp" snippet="qeasingcurve"/>
<enum-type name="Type"/>
- <add-function signature="setCustomType(PyObject*)">
+ <add-function signature="setCustomType(PyObject*@callable@)">
<inject-code file="../glue/qtcore.cpp" snippet="qeasingcurve-setcustomtype"/>
</add-function>
<add-function signature="customType()" return-type="PyObject">
<!-- From Qt4.6 ^^^ -->
- <add-function signature="SIGNAL(const char*)" return-type="str">
+ <add-function signature="SIGNAL(const char*@signature@)" return-type="str">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qt-signal"/>
<modify-argument index="1" pyi-type="str"/>
</add-function>
- <add-function signature="SLOT(const char*)" return-type="str">
+ <add-function signature="SLOT(const char*@signature@)" return-type="str">
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qt-slot"/>
<modify-argument index="1" pyi-type="str"/>
</add-function>
- <add-function signature="QT_TR_NOOP(PyObject)" return-type="PyObject*">
+ <add-function signature="QT_TR_NOOP(PyObject@message@)" return-type="PyObject*">
<inject-code class="target" position="beginning">
<insert-template name="return_argument"><replace from="#" to="1"/></insert-template>
</inject-code>
</add-function>
- <add-function signature="QT_TR_NOOP_UTF8(PyObject)" return-type="PyObject*">
+ <add-function signature="QT_TR_NOOP_UTF8(PyObject@message@)" return-type="PyObject*">
<inject-code class="target" position="beginning">
<insert-template name="return_argument"><replace from="#" to="1"/></insert-template>
</inject-code>
</add-function>
- <add-function signature="QT_TRANSLATE_NOOP(PyObject,PyObject)" return-type="PyObject*">
+ <add-function signature="QT_TRANSLATE_NOOP(PyObject@context@,PyObject@message@)" return-type="PyObject*">
<inject-code class="target" position="beginning">
<insert-template name="return_argument"><replace from="#" to="2"/></insert-template>
</inject-code>
</add-function>
- <add-function signature="QT_TRANSLATE_NOOP3(PyObject,PyObject,PyObject)" return-type="PyObject*">
+ <add-function signature="QT_TRANSLATE_NOOP3(PyObject@context@,PyObject@message@,PyObject@disambiguation@)" return-type="PyObject*">
<inject-code class="target" position="beginning">
<insert-template name="return_argument"><replace from="#" to="2"/></insert-template>
</inject-code>
</add-function>
- <add-function signature="QT_TRANSLATE_NOOP_UTF8(PyObject)" return-type="PyObject*">
+ <add-function signature="QT_TRANSLATE_NOOP_UTF8(PyObject@message@)" return-type="PyObject*">
<inject-code class="target" position="beginning">
<insert-template name="return_argument"><replace from="#" to="1"/></insert-template>
</inject-code>
<object-type name="QLoggingCategory"/>
- <add-function signature="qCDebug(PyObject *, const char *)">
+ <add-function signature="qCDebug(QLoggingCategory *@logging_category@, const char *@message@)">
<extra-includes>
<include file-name="qloggingcategory.h" location="global" />
</extra-includes>
- <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qloggingcategory_to_cpp">
- qCDebug((*category), "%s", %2);
+ <inject-code class="target" position="beginning">
+ qCDebug(*%1, "%s", %2);
</inject-code>
</add-function>
- <add-function signature="qCCritical(PyObject *, const char *)">
- <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qloggingcategory_to_cpp">
- qCCritical((*category), "%s", %2);
+ <add-function signature="qCCritical(QLoggingCategory *@logging_category@, const char *@message@)">
+ <inject-code class="target" position="beginning">
+ qCCritical(*%1, "%s", %2);
</inject-code>
</add-function>
- <add-function signature="qCInfo(PyObject *, const char *)">
- <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qloggingcategory_to_cpp">
- qCInfo((*category), "%s", %2);
+ <add-function signature="qCInfo(QLoggingCategory *@logging_category@, const char *@message@)">
+ <inject-code class="target" position="beginning">
+ qCInfo(*%1, "%s", %2);
</inject-code>
</add-function>
- <add-function signature="qCWarning(PyObject *, const char *)">
- <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qloggingcategory_to_cpp">
- qCWarning((*category), "%s", %2);
+ <add-function signature="qCWarning(QLoggingCategory *@logging_category@, const char *@message@)">
+ <inject-code class="target" position="beginning">
+ qCWarning(*%1, "%s", %2);
</inject-code>
</add-function>
<enum-type name="ErrorType"/>
</value-type>
<object-type name="QDBusInterface" qt-metaobject="no">
- <inject-documentation format="target" mode="append">
+ <inject-documentation format="target" mode="append" emphasis="language-note">
DBus signals can be captured with string-based connections
(see :ref:`signals-and-slots-strings`).
</inject-documentation>
<enum-type name="WatchModeFlag" flags="WatchMode"/>
</object-type>
<object-type name="QDBusSignature"/>
- <object-type name="QDBusUnixFileDescriptor"/>
+ <value-type name="QDBusUnixFileDescriptor"/>
<value-type name="QDBusVariant"/>
<object-type name="QDBusVirtualObject"/>
<suppress-warning text='^.*Unable to translate type "QDBusReply<.*$'/>
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtDataVisualization"
+<typesystem package="PySide6.QtDataVisualization" doc-mode="flat"
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<extra-includes>
}
Shiboken::AutoDecRef pyArgs(PyTuple_New(1));
- PyTuple_SET_ITEM(pyArgs, 0, pyParent); // tuple will keep pyParent reference
+ PyTuple_SetItem(pyArgs, 0, pyParent); // tuple will keep pyParent reference
// Call python constructor
auto result = reinterpret_cast<SbkObject *>(PyObject_CallObject(m_pyTypeObject, pyArgs));
# 3D
${QtGraphs_GEN_DIR}/qabstract3daxis_wrapper.cpp
${QtGraphs_GEN_DIR}/qcategory3daxis_wrapper.cpp
+${QtGraphs_GEN_DIR}/qtgraphs3d_wrapper.cpp
+${QtGraphs_GEN_DIR}/qgraphsline_wrapper.cpp
+${QtGraphs_GEN_DIR}/qgraphstheme_wrapper.cpp
+${QtGraphs_GEN_DIR}/qgraphsthemedirtybitfield_wrapper.cpp
${QtGraphs_GEN_DIR}/qlogvalue3daxisformatter_wrapper.cpp
${QtGraphs_GEN_DIR}/qvalue3daxis_wrapper.cpp
${QtGraphs_GEN_DIR}/qvalue3daxisformatter_wrapper.cpp
${QtGraphs_GEN_DIR}/qitemmodelbardataproxy_wrapper.cpp
${QtGraphs_GEN_DIR}/qitemmodelscatterdataproxy_wrapper.cpp
${QtGraphs_GEN_DIR}/qitemmodelsurfacedataproxy_wrapper.cpp
+${QtGraphs_GEN_DIR}/qlegenddata_wrapper.cpp
${QtGraphs_GEN_DIR}/qscatter3dseries_wrapper.cpp
${QtGraphs_GEN_DIR}/qscatterdataitem_wrapper.cpp
${QtGraphs_GEN_DIR}/qscatterdataproxy_wrapper.cpp
${QtGraphs_GEN_DIR}/qsurface3dseries_wrapper.cpp
${QtGraphs_GEN_DIR}/qsurfacedataitem_wrapper.cpp
${QtGraphs_GEN_DIR}/qsurfacedataproxy_wrapper.cpp
-${QtGraphs_GEN_DIR}/q3dbars_wrapper.cpp
-${QtGraphs_GEN_DIR}/q3dscatter_wrapper.cpp
${QtGraphs_GEN_DIR}/q3dscene_wrapper.cpp
-${QtGraphs_GEN_DIR}/q3dsurface_wrapper.cpp
-${QtGraphs_GEN_DIR}/qabstract3dgraph_wrapper.cpp
-${QtGraphs_GEN_DIR}/q3dinputhandler_wrapper.cpp
-${QtGraphs_GEN_DIR}/qabstract3dinputhandler_wrapper.cpp
-${QtGraphs_GEN_DIR}/qtouch3dinputhandler_wrapper.cpp
-${QtGraphs_GEN_DIR}/q3dtheme_wrapper.cpp
# 2D
-${QtGraphs_GEN_DIR}/qbarcategoryaxis_wrapper.cpp
${QtGraphs_GEN_DIR}/qabstractaxis_wrapper.cpp
-${QtGraphs_GEN_DIR}/qvalueaxis_wrapper.cpp
-${QtGraphs_GEN_DIR}/qabstractbarseries_wrapper.cpp
+${QtGraphs_GEN_DIR}/qabstractseries_wrapper.cpp
+${QtGraphs_GEN_DIR}/qareaseries_wrapper.cpp
+${QtGraphs_GEN_DIR}/qbarcategoryaxis_wrapper.cpp
+${QtGraphs_GEN_DIR}/qbarmodelmapper_wrapper.cpp
${QtGraphs_GEN_DIR}/qbarseries_wrapper.cpp
${QtGraphs_GEN_DIR}/qbarset_wrapper.cpp
+${QtGraphs_GEN_DIR}/qdatetimeaxis_wrapper.cpp
${QtGraphs_GEN_DIR}/qlineseries_wrapper.cpp
-${QtGraphs_GEN_DIR}/qabstractseries_wrapper.cpp
+${QtGraphs_GEN_DIR}/qpiemodelmapper_wrapper.cpp
+${QtGraphs_GEN_DIR}/qpieseries_wrapper.cpp
+${QtGraphs_GEN_DIR}/qpieslice_wrapper.cpp
${QtGraphs_GEN_DIR}/qscatterseries_wrapper.cpp
-${QtGraphs_GEN_DIR}/qgraphtheme_wrapper.cpp
-${QtGraphs_GEN_DIR}/qseriestheme_wrapper.cpp
+${QtGraphs_GEN_DIR}/qsplineseries_wrapper.cpp
+${QtGraphs_GEN_DIR}/qvalueaxis_wrapper.cpp
+${QtGraphs_GEN_DIR}/qxymodelmapper_wrapper.cpp
${QtGraphs_GEN_DIR}/qxyseries_wrapper.cpp
# module is always needed
${QtGraphs_GEN_DIR}/qtgraphs_module_wrapper.cpp
${Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}QtNetwork_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Gui_INCLUDE_DIRS}
- ${Qt${QT_MAJOR_VERSION}Widgets_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Qml_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Quick_INCLUDE_DIRS}
- ${Qt${QT_MAJOR_VERSION}QuickWidgets_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Graphs_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Quick3D_INCLUDE_DIRS}
${libpyside_SOURCE_DIR}
${QtCore_GEN_DIR}
${QtNetwork_GEN_DIR}
${QtGui_GEN_DIR}
- ${QtWidgets_GEN_DIR}
${QtQml_GEN_DIR}
${QtQuick_GEN_DIR}
- ${QtQuickWidgets_GEN_DIR}
${QtQuick3D_GEN_DIR})
set(QtGraphs_libraries pyside6
${Qt${QT_MAJOR_VERSION}Graphs_LIBRARIES})
-set(QtGraphs_deps QtCore QtNetwork QtGui QtWidgets QtQml QtQuick QtQuickWidgets QtQuick3D)
+set(QtGraphs_deps QtCore QtNetwork QtGui QtQml QtQuick QtQuick3D)
check_qt_opengl("Graphs" QtGraphs_include_dirs QtGraphs_deps
QtGraphs_DROPPED_ENTRIES)
<include file-name="qtgraphs_helper.h" location="global"/>
</extra-includes>
+ <load-typesystem name="QtQuick/typesystem_quick.xml" generate="no" />
+
<load-typesystem name="templates/datavisualization_common.xml" generate="no" />
- <load-typesystem name="QtQuickWidgets/typesystem_quickwidgets.xml" generate="no" />
<function signature="qDefaultSurfaceFormat(bool)"/>
+ <namespace-type name="QtGraphs3D">
+ <enum-type name="ElementType"/>
+ <enum-type name="GridLineType"/>
+ <enum-type name="OptimizationHint" flags="OptimizationHints"/>
+ <enum-type name="RenderingMode"/>
+ <enum-type name="SelectionFlag" flags="SelectionFlags"/>
+ <enum-type name="ShadowQuality"/>
+ <enum-type name="CameraPreset" since="6.7"/>
+ </namespace-type>
+
+ <value-type name="QGraphsLine" since="6.8">
+ <modify-function signature="create(QJSValue)" remove="all"/>
+ <modify-function signature="swap(QGraphsLine&)" remove="all"/>
+ </value-type>
<object-type name="QAbstract3DAxis">
<enum-type name="AxisOrientation"/>
<object-type name="QAbstractDataProxy">
<enum-type name="DataType"/>
</object-type>
+ <object-type name="QAreaSeries" since="6.8"/>
<object-type name="QBar3DSeries">
<modify-function signature="setDataProxy(QBarDataProxy*)">
<modify-argument index="1">
</object-type>
<value-type name="QBarDataItem"/>
<object-type name="QBarDataProxy">
+ <enum-type name="RemoveLabels" since="6.8"/>
</object-type>
+ <object-type name="QBarModelMapper" since="6.8"/>
<object-type name="QCustom3DItem"/>
<object-type name="QCustom3DLabel"/>
<object-type name="QCustom3DVolume">
snippet="qcustom3dvolume-settexturedata"/>
</add-function>
</object-type>
+ <object-type name="QDateTimeAxis" since="6.8"/>
<object-type name="QHeightMapSurfaceDataProxy"/>
<object-type name="QItemModelBarDataProxy">
<enum-type name="MultiMatchBehavior"/>
<object-type name="QItemModelSurfaceDataProxy">
<enum-type name="MultiMatchBehavior"/>
</object-type>
+ <value-type name="QLegendData" since="6.8"/>
<object-type name="QScatter3DSeries">
<modify-function signature="setDataProxy(QScatterDataProxy*)">
<modify-argument index="1">
</modify-argument>
</modify-function>
</object-type>
+ <object-type name="QPieModelMapper" since="6.8"/>
+ <object-type name="QPieSeries" since="6.8"/>
+ <object-type name="QPieSlice" since="6.8">
+ <enum-type name="LabelPosition"/>
+ </object-type>
<value-type name="QScatterDataItem"/>
<object-type name="QScatterDataProxy">
</object-type>
+ <object-type name="QSplineSeries" since="6.8"/>
<object-type name="QSurface3DSeries">
+ <enum-type name="Shading" since="6.8"/>
<enum-type name="DrawFlag" flags="DrawFlags"/>
<modify-function signature="QSurface3DSeries(QSurfaceDataProxy*,QObject*)">
<modify-argument index="1">
``deltaZ``, respectively.
</inject-documentation>
</add-function>
-
- </object-type>
- <object-type name="Q3DBars">
- <modify-function signature="addAxis(QAbstract3DAxis*)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="releaseAxis(QAbstract3DAxis*)">
- <modify-argument index="1">
- <reference-count action="set"/>
- </modify-argument>
- <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
- </modify-function>
- <modify-function signature="setColumnAxis(QCategory3DAxis*)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="setRowAxis(QCategory3DAxis*)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- </object-type>
- <object-type name="Q3DScatter">
- <modify-function signature="addAxis(QValue3DAxis*)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="releaseAxis(QValue3DAxis*)">
- <modify-argument index="1">
- <reference-count action="set"/>
- </modify-argument>
- <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
- </modify-function>
- <modify-function signature="setAxisX(QValue3DAxis*)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="setAxisY(QValue3DAxis*)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="setAxisZ(QValue3DAxis*)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
</object-type>
<object-type name="Q3DScene"/>
- <object-type name="Q3DSurface">
- <modify-function signature="addAxis(QValue3DAxis*)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="releaseAxis(QValue3DAxis*)">
- <modify-argument index="1">
- <reference-count action="set"/>
- </modify-argument>
- <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
- </modify-function>
- <modify-function signature="setAxisX(QValue3DAxis*)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="setAxisY(QValue3DAxis*)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="setAxisZ(QValue3DAxis*)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- </object-type>
- <object-type name="QAbstract3DGraph">
- <enum-type name="ElementType"/>
- <enum-type name="OptimizationHint" flags="OptimizationHints"/>
- <enum-type name="RenderingMode"/>
- <enum-type name="SelectionFlag" flags="SelectionFlags"/>
- <enum-type name="ShadowQuality"/>
- <enum-type name="CameraPreset" since="6.7"/>
- <modify-function signature="addCustomItem(QCustom3DItem*)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="addInputHandler(QAbstract3DInputHandler*)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="addTheme(Q3DTheme*)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="releaseCustomItem(QCustom3DItem*)">
- <modify-argument index="1">
- <reference-count action="set"/>
- </modify-argument>
- <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
- </modify-function>
- <modify-function signature="releaseInputHandler(QAbstract3DInputHandler*)">
- <modify-argument index="1">
- <reference-count action="set"/>
- </modify-argument>
- <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
- </modify-function>
- <modify-function signature="releaseTheme(Q3DTheme*)">
- <modify-argument index="1">
- <reference-count action="set"/>
- </modify-argument>
- <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
- </modify-function>
- <modify-function signature="setActiveInputHandler(QAbstract3DInputHandler*)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- <modify-function signature="setActiveTheme(Q3DTheme*)">
- <modify-argument index="1">
- <parent index="this" action="add"/>
- </modify-argument>
- </modify-function>
- </object-type>
- <object-type name="Q3DInputHandler"/>
- <object-type name="QAbstract3DInputHandler">
- <enum-type name="InputView"/>
- </object-type>
- <object-type name="QTouch3DInputHandler"/>
- <object-type name="Q3DTheme">
+ <object-type name="QGraphsTheme" since="6.8">
+ <enum-type name="ColorScheme"/>
<enum-type name="ColorStyle"/>
+ <enum-type name="ForceTheme"/>
<enum-type name="Theme"/>
</object-type>
+ <value-type name="QGraphsThemeDirtyBitField" since="6.8"/>
<!-- 2D -->
<object-type name="QBarCategoryAxis"/>
<enum-type name="AxisType"/>
</object-type>
<object-type name="QValueAxis"/>
- <object-type name="QAbstractBarSeries">
+ <object-type name="QBarSeries">
<enum-type name="LabelsPosition"/>
+ <enum-type name="BarsType" since="6.8"/>
<modify-function signature="append(QBarSet*)">
<modify-argument index="1">
<parent index="this" action="add"/>
<parent index="this" action="add"/>
</modify-argument>
</modify-function>
- <modify-function signature="insert(int,QBarSet*)">
+ <modify-function signature="insert(qsizetype,QBarSet*)">
<modify-argument index="2">
<parent index="this" action="add"/>
</modify-argument>
</modify-argument>
</modify-function>
</object-type>
- <object-type name="QBarSeries"/>
<object-type name="QBarSet"/>
<object-type name="QLineSeries"/>
<object-type name="QAbstractSeries">
<enum-type name="SeriesType"/>
</object-type>
<object-type name="QScatterSeries"/>
- <object-type name="QGraphTheme">
- <enum-type name="ColorTheme"/>
- </object-type>
- <object-type name="QSeriesTheme">
- <enum-type name="SeriesColorTheme"/>
- </object-type>
+ <object-type name="QXYModelMapper" since="6.8"/>
<object-type name="QXYSeries">
+ <extra-includes>
+ <include file-name="pyside_numpy.h" location="global"/>
+ </extra-includes>
+ <add-function signature="appendNp(PyArrayObject *@x@, PyArrayObject *@y@)">
+ <inject-code file="../glue/qtcharts.cpp" snippet="qxyseries-appendnp-numpy-x-y"/>
+ <inject-documentation format="target" mode="append">
+ Adds the list of data points specified by two
+ one-dimensional, equally sized numpy arrays representing the x, y values, respectively.
+ </inject-documentation>
+ </add-function>
+ <add-function signature="replaceNp(PyArrayObject *@x@, PyArrayObject *@y@)">
+ <inject-code file="../glue/qtcharts.cpp" snippet="qxyseries-replacenp-numpy-x-y"/>
+ <inject-documentation format="target" mode="append">
+ Replaces the current points with the points specified by two
+ one-dimensional, equally sized numpy arrays representing the x, y values, respectively.
+ </inject-documentation>
+ </add-function>
</object-type>
<extra-includes>
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+project(QtGraphsWidgets)
+
+set (QtGraphsWidgets_DROPPED_ENTRIES)
+
+set(QtGraphsWidgets_SRC
+${QtGraphsWidgets_GEN_DIR}/q3dbarswidgetitem_wrapper.cpp
+${QtGraphsWidgets_GEN_DIR}/q3dgraphswidgetitem_wrapper.cpp
+${QtGraphsWidgets_GEN_DIR}/q3dscatterwidgetitem_wrapper.cpp
+${QtGraphsWidgets_GEN_DIR}/q3dsurfacewidgetitem_wrapper.cpp
+
+# module is always needed
+${QtGraphsWidgets_GEN_DIR}/qtgraphswidgets_module_wrapper.cpp
+)
+
+set(QtGraphsWidgets_include_dirs ${QtGraphsWidgets_SOURCE_DIR}
+ ${QtGraphsWidgets_BINARY_DIR}
+ ${Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}QtNetwork_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}Gui_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}Widgets_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}Graphs_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}Qml_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}Quick_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}QuickWidgets_INCLUDE_DIRS}
+ ${QtCore_GEN_DIR}
+ ${QtGui_GEN_DIR}
+ ${QtNetwork_GEN_DIR}
+ ${QtWidgets_GEN_DIR}
+ ${QtGraphs_GEN_DIR}
+ ${QtQml_GEN_DIR}
+ ${QtQuick_GEN_DIR}
+ ${QtQuickWidgets_GEN_DIR}
+ ${libpyside_SOURCE_DIR}
+)
+
+set(QtGraphsWidgets_libraries pyside6
+ ${Qt${QT_MAJOR_VERSION}GraphsWidgets_LIBRARIES})
+
+set(QtGraphsWidgets_deps QtQuickWidgets QtGraphs)
+
+check_qt_opengl("GraphsWidgets" QtGraphsWidgets_include_dirs QtGraphsWidgets_deps
+ QtGraphsWidgets_DROPPED_ENTRIES)
+
+create_pyside_module(NAME QtGraphsWidgets
+ INCLUDE_DIRS QtGraphsWidgets_include_dirs
+ LIBRARIES QtGraphsWidgets_libraries
+ DEPS QtGraphsWidgets_deps
+ TYPESYSTEM_PATH QtGraphsWidgets_SOURCE_DIR
+ SOURCES QtGraphsWidgets_SRC
+ STATIC_SOURCES QtGraphsWidgets_src
+ DROPPED_ENTRIES QtGraphsWidgets_DROPPED_ENTRIES)
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+-->
+<typesystem package="PySide6.QtGraphsWidgets" doc-package="PySide6.QtGraphs"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
+
+ <load-typesystem name="QtQuickWidgets/typesystem_quickwidgets.xml" generate="no" />
+ <load-typesystem name="QtGraphs/typesystem_graphs.xml" generate="no" />
+
+ <object-type name="Q3DBarsWidgetItem">
+ <modify-function signature="addAxis(QAbstract3DAxis*)">
+ <modify-argument index="1">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="releaseAxis(QAbstract3DAxis*)">
+ <modify-argument index="1">
+ <reference-count action="set"/>
+ </modify-argument>
+ <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
+ </modify-function>
+ <modify-function signature="setColumnAxis(QCategory3DAxis*)">
+ <modify-argument index="1">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="setRowAxis(QCategory3DAxis*)">
+ <modify-argument index="1">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ </object-type>
+
+ <object-type name="Q3DScatterWidgetItem">
+ <modify-function signature="addAxis(QValue3DAxis*)">
+ <modify-argument index="1">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="releaseAxis(QValue3DAxis*)">
+ <modify-argument index="1">
+ <reference-count action="set"/>
+ </modify-argument>
+ <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
+ </modify-function>
+ <modify-function signature="setAxisX(QValue3DAxis*)">
+ <modify-argument index="1">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="setAxisY(QValue3DAxis*)">
+ <modify-argument index="1">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="setAxisZ(QValue3DAxis*)">
+ <modify-argument index="1">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ </object-type>
+
+ <object-type name="Q3DSurfaceWidgetItem">
+ <modify-function signature="addAxis(QValue3DAxis*)">
+ <modify-argument index="1">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="releaseAxis(QValue3DAxis*)">
+ <modify-argument index="1">
+ <reference-count action="set"/>
+ </modify-argument>
+ <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
+ </modify-function>
+ <modify-function signature="setAxisX(QValue3DAxis*)">
+ <modify-argument index="1">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="setAxisY(QValue3DAxis*)">
+ <modify-argument index="1">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="setAxisZ(QValue3DAxis*)">
+ <modify-argument index="1">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ </object-type>
+
+ <object-type name="Q3DGraphsWidgetItem">
+ <modify-function signature="addCustomItem(QCustom3DItem*)">
+ <modify-argument index="1">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="addTheme(QGraphsTheme*)">
+ <modify-argument index="1">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="releaseCustomItem(QCustom3DItem*)">
+ <modify-argument index="1">
+ <reference-count action="set"/>
+ </modify-argument>
+ <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
+ </modify-function>
+ <modify-function signature="releaseTheme(QGraphsTheme*)">
+ <modify-argument index="1">
+ <reference-count action="set"/>
+ </modify-argument>
+ <inject-code file="../glue/qtdatavisualization.cpp" snippet="releaseownership"/>
+ </modify-function>
+ <modify-function signature="setActiveTheme(QGraphsTheme*)">
+ <modify-argument index="1">
+ <parent index="this" action="add"/>
+ </modify-argument>
+ </modify-function>
+ </object-type>
+
+</typesystem>
${QtGui_GEN_DIR}/qrhitexturesubresourceuploaddescription_wrapper.cpp
${QtGui_GEN_DIR}/qrhitextureuploaddescription_wrapper.cpp
${QtGui_GEN_DIR}/qrhitextureuploadentry_wrapper.cpp
+${QtGui_GEN_DIR}/qrhitexture_viewformat_wrapper.cpp
${QtGui_GEN_DIR}/qrhivertexinputattribute_wrapper.cpp
${QtGui_GEN_DIR}/qrhivertexinputbinding_wrapper.cpp
${QtGui_GEN_DIR}/qrhivertexinputlayout_wrapper.cpp
${QtGui_GEN_DIR}/qabstracttextdocumentlayout_wrapper.cpp
${QtGui_GEN_DIR}/qaccessible_wrapper.cpp
${QtGui_GEN_DIR}/qaccessibleactioninterface_wrapper.cpp
+${QtGui_GEN_DIR}/qaccessibleannouncementevent_wrapper.cpp
+${QtGui_GEN_DIR}/qaccessibleattributesinterface_wrapper.cpp
${QtGui_GEN_DIR}/qaccessibleeditabletextinterface_wrapper.cpp
${QtGui_GEN_DIR}/qaccessibleevent_wrapper.cpp
${QtGui_GEN_DIR}/qaccessibleobject_wrapper.cpp
${QtGui_GEN_DIR}/qpainterpathstroker_wrapper.cpp
${QtGui_GEN_DIR}/qpaintevent_wrapper.cpp
${QtGui_GEN_DIR}/qpalette_wrapper.cpp
+${QtGui_GEN_DIR}/qpdfoutputintent_wrapper.cpp
${QtGui_GEN_DIR}/qpdfwriter_wrapper.cpp
${QtGui_GEN_DIR}/qpen_wrapper.cpp
${QtGui_GEN_DIR}/qpicture_wrapper.cpp
${Qt${QT_MAJOR_VERSION}Core_PRIVATE_INCLUDE_DIRS}
${Qt${QT_MAJOR_VERSION}Gui_PRIVATE_INCLUDE_DIRS})
+if (${CMAKE_SYSTEM_NAME} STREQUAL "Android")
+ if (QT_FEATURE_opengles2)
+ # add openGL ES 2.0
+ find_package(GLESv2 REQUIRED)
+ else()
+ message(FATAL_ERROR "QtGui requires OpenGL ES 2.0 on Android")
+ endif()
+endif()
+
configure_file("${QtGui_SOURCE_DIR}/QtGui_global.post.h.in"
"${QtGui_BINARY_DIR}/QtGui_global.post.h" @ONLY)
install(FILES ${pyside6_SOURCE_DIR}/qpytextobject.h
${pyside6_SOURCE_DIR}/qtguihelper.h
DESTINATION include/PySide6/QtGui/)
-
<!-- For Qt::mightBeRichText(QString), Qt::convertFromPlainText(QString,Qt::WhiteSpaceMode)
Match on files from '/QtGui/' (shadow build) or '/gui/' (developer build)
or '/QtGui.framework' (macOS) -->
- <namespace-type name="Qt" files="^.*/(gui|QtGui)[/.].*\.h$" extends="PySide6.QtCore"/>
+ <namespace-type name="Qt" files="^.*/(gui|QtGui)[/.].*\.h$" extends="PySide6.QtCore"
+ doc-file="qt-sub-qtgui"/>
<primitive-type name="WId" target-lang-api-name="PyLong">
<conversion-rule>
file="../glue/qtgui.cpp"
snippet="qaccessible-pysidefactory"/>
<value-type name="State"/>
+ <enum-type name="AnnouncementPoliteness" since="6.8"/>
<enum-type name="Event"/>
<enum-type name="Role"/>
<enum-type name="Text"/>
<enum-type name="RelationFlag" flags="Relation"/>
<enum-type name="InterfaceType"/>
<enum-type name="TextBoundaryType"/>
+ <enum-type name="Attribute" since="6.8"/>
<add-function signature="installFactory(PyCallable)" static="yes">
<inject-code class="target" position="beginning"
file="../glue/qtgui.cpp"
</value-type>
<object-type name="QAccessibleActionInterface"/>
+ <object-type name="QAccessibleAttributesInterface" since="6.8"/>
<object-type name="QAccessibleEditableTextInterface"/>
<object-type name="QAccessibleInterface"/>
<object-type name="QAccessibleObject" qt-register-metatype="base"/>
<object-type name="QAccessibleEvent"
polymorphic-id-expression="%B->type() == QAccessible::InvalidEvent"/>
+ <object-type name="QAccessibleAnnouncementEvent"
+ polymorphic-id-expression="%B->type() == QAccessible::Announcement"/>
<object-type name="QAccessibleStateChangeEvent"
polymorphic-id-expression="%B->type() == QAccessible::StateChanged"/>
<object-type name="QAccessibleTableModelChangeEvent"
</modify-function>
</object-type>
<object-type name="QShortcut">
- <add-function signature="QShortcut(QKeySequence&,QObject*,PyCallable*,Qt::ShortcutContext=Qt::WindowShortcut)">
+ <add-function signature="QShortcut(QKeySequence&@key@,QObject*@parent@,PyCallable*@callable@,Qt::ShortcutContext@context@=Qt::WindowShortcut)">
<inject-code class="target" position="beginning" file="../glue/qtgui.cpp" snippet="qshortcut-1"/>
<inject-code class="target" position="end" file="../glue/qtgui.cpp" snippet="qshortcut-2"/>
</add-function>
- <add-function signature="QShortcut(QKeySequence::StandardKey,QObject*,PyCallable*,Qt::ShortcutContext=Qt::WindowShortcut)">
+ <add-function signature="QShortcut(QKeySequence::StandardKey@standard_key@,QObject*@parent@,PyCallable*@callable@,Qt::ShortcutContext@context@=Qt::WindowShortcut)">
<inject-code class="target" position="beginning" file="../glue/qtgui.cpp" snippet="qshortcut-1"/>
<inject-code class="target" position="end" file="../glue/qtgui.cpp" snippet="qshortcut-2"/>
</add-function>
<value-type name="QTransform">
<enum-type name="TransformationType"/>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="%f, %f, %f, %f, %f, %f, %f, %f, %f"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="ddddddddd"/>
<insert-template name="fix_bool*"/>
</inject-code>
</modify-function>
- <add-function signature="quadToQuad(QPolygonF&,QPolygonF&)" return-type="PyObject*" static="true">
+ <add-function signature="quadToQuad(QPolygonF&@one@,QPolygonF&@two@)" return-type="PyObject*" static="true">
<inject-code file="../glue/qtgui.cpp" snippet="qtransform-quadtoquad"/>
</add-function>
<add-function signature="quadToSquare(QPolygonF &)" return-type="PyObject*" static="true">
<enum-type name="SequenceMatch"/>
<enum-type name="StandardKey"/>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning" file="../glue/qtgui.cpp" snippet="qkeysequence-repr"/>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="iiii"/>
</value-type>
<value-type name="QRegion" >
<enum-type name="RegionType"/>
- <add-function signature="__len__">
+ <add-function signature="__len__" return-type="int">
<inject-code class="target" position="beginning" file="../glue/qtgui.cpp" snippet="qregion-len"/>
</add-function>
<add-function signature="__getitem__">
<add-function signature="operator==(const QPolygon&)" return-type="bool"/>
<add-function signature="operator!=(const QPolygon&)" return-type="bool"/>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning" file="../glue/qtgui.cpp" snippet="qpolygon-reduce">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="N"/>
<inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qfile-path-1"/>
</modify-function>
- <add-function signature="QImage(QString&,int,int,int,QImage::Format)">
+ <add-function signature="QImage(QString&,int@width@,int@height@,int@bytes_per_line@,QImage::Format@format@)">
<inject-code>
<insert-template name="qimage_buffer_constructor">
<replace from="%ARGS" to="%2, %3, %4, %5"/>
</insert-template>
</inject-code>
</add-function>
- <add-function signature="QImage(QString&,int,int,QImage::Format)">
+ <add-function signature="QImage(QString&,int@width@,int@height@,QImage::Format@format@)">
<inject-code>
<insert-template name="qimage_buffer_constructor">
<replace from="%ARGS" to="%2, %3, %4"/>
<extra-includes>
<include file-name="QVariant" location="global"/>
</extra-includes>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="qcolor_repr"/>
</inject-code>
</add-function>
- <add-function signature="__str__" return-type="PyObject*">
+ <add-function signature="__str__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="qcolor_repr"/>
</inject-code>
<add-function signature="__setstate__(PyObject*)" return-type="PyObject">
<inject-code file="../glue/qtgui.cpp" snippet="qcolor-setstate"/>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning" file="../glue/qtgui.cpp" snippet="qcolor-reduce"/>
</add-function>
<!-- ### Constructor removed because we already have an overload using QString. -->
<modify-function signature="QColor(const char*)" remove="all"/>
<!-- ### -->
- <add-function signature="QColor(QVariant)">
+ <add-function signature="QColor(QVariant@color@)">
<inject-code class="target" position="beginning" file="../glue/qtgui.cpp" snippet="qcolor"/>
</add-function>
<!-- get* methods. Inject code -->
</namespace-type>
<value-type name="QColorSpace">
+ <enum-type name="ColorModel" since="6.8"/>
<enum-type name="NamedColorSpace"/>
<enum-type name="Primaries"/>
<enum-type name="TransferFunction"/>
+ <enum-type name="TransformModel" since="6.8"/>
</value-type>
<value-type name="QColorTransform"/>
<value-type name="QLinearGradient" polymorphic-id-expression="%B->type() == QGradient::LinearGradient"/>
<object-type name="QPaintDevice">
<enum-type name="PaintDeviceMetric"/>
+ <!-- inline error -->
+ <modify-function signature="getDecodedMetricF(QPaintDevice::PaintDeviceMetric,QPaintDevice::PaintDeviceMetric)const" remove="all"/>
</object-type>
<object-type name="QPagedPaintDevice">
<enum-type name="PdfVersion"/>
<object-type name="QPainterPathStroker" copyable="false"/>
<object-type name="QPixmapCache">
<value-type name="Key"/>
- <add-function signature="find(QPixmapCache::Key&)">
+ <add-function signature="find(QPixmapCache::Key&@key@)">
<inject-code file="../glue/qtgui.cpp" snippet="qpixmapcache-find"/>
</add-function>
- <add-function signature="find(const QString&)">
+ <add-function signature="find(const QString&@key@)">
<inject-code file="../glue/qtgui.cpp" snippet="qpixmapcache-find"/>
</add-function>
</object-type>
<enum-type name="State"/>
</value-type>
<object-type name="QDragEnterEvent" polymorphic-id-expression="%B->type() == QEvent::DragEnter">
- <add-function signature="__repr__" return-type="PyObject">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
</add-function>
</object-type>
<object-type name="QDragLeaveEvent" polymorphic-id-expression="%B->type() == QEvent::DragLeave">
- <add-function signature="__repr__" return-type="PyObject">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
</add-function>
</object-type>
<object-type name="QDragMoveEvent" polymorphic-id-expression="%B->type() == QEvent::DragMove">
- <add-function signature="__repr__" return-type="PyObject">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
<define-ownership class="target" owner="default"/>
</modify-argument>
</modify-function>
- <add-function signature="__repr__" return-type="PyObject">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
<object-type name="QFileOpenEvent" polymorphic-id-expression="%B->type() == QEvent::FileOpen"/>
<object-type name="QFocusEvent"
polymorphic-id-expression="%B->type() == QEvent::FocusIn || %B->type() == QEvent::FocusOut">
- <add-function signature="__repr__" return-type="PyObject">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
<value-type name="Attribute">
<include file-name="QInputMethodEvent" location="global"/>
</value-type>
- <add-function signature="__repr__" return-type="PyObject">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
polymorphic-id-expression="%B->type() == QEvent::InputMethodQuery"/>
<object-type name="QMoveEvent" polymorphic-id-expression="%B->type() == QEvent::Move">
- <add-function signature="__repr__" return-type="PyObject">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
<enum-type name="SurfaceEventType"/>
</object-type>
<object-type name="QResizeEvent" polymorphic-id-expression="%B->type() == QEvent::Resize">
- <add-function signature="__repr__" return-type="PyObject">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
polymorphic-id-expression="%B->type() == QEvent::StatusTip"/>
<object-type name="QTabletEvent"
polymorphic-id-expression="%B->type() == QEvent::TabletMove || %B->type() == QEvent::TabletPress || %B->type() == QEvent::TabletRelease">
- <add-function signature="__repr__" return-type="PyObject">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
<object-type name="QWhatsThisClickedEvent"
polymorphic-id-expression="%B->type() == QEvent::WhatsThisClicked"/>
<object-type name="QWheelEvent" polymorphic-id-expression="%B->type() == QEvent::Wheel">
- <add-function signature="__repr__" return-type="PyObject">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
<object-type name="QWindowStateChangeEvent"
polymorphic-id-expression="%B->type() == QEvent::WindowStateChange">
- <add-function signature="__repr__" return-type="PyObject">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
<object-type name="QInputEvent"/>
<object-type name="QKeyEvent"
polymorphic-id-expression="%B->type() == QEvent::KeyPress || %B->type() == QEvent::KeyRelease || %B->type() == QEvent::ShortcutOverride">
- <add-function signature="operator!=(QKeySequence::StandardKey)">
+ <add-function signature="operator!=(QKeySequence::StandardKey@standard_key@)">
<inject-code class="target" file="../glue/qtgui.cpp" snippet="qkeyevent-operatornotequal"/>
</add-function>
- <add-function signature="__repr__" return-type="PyObject">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
<modify-function signature="y() const" deprecated="yes"/>
<modify-function signature="globalX() const" deprecated="yes"/>
<modify-function signature="globalY() const" deprecated="yes"/>
- <add-function signature="__repr__" return-type="PyObject">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
<enum-type name="ScrollState"/>
</object-type>
<object-type name="QPointerEvent" copyable= "false">
- <add-function signature="__repr__" return-type="PyObject">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
<modify-function signature="drawConvexPolygon(const QPoint*,int)" remove="all"/>
- <add-function signature="drawConvexPolygon(QList<QPoint>)">
+ <add-function signature="drawConvexPolygon(QList<QPoint>@points@)">
<inject-code>
<insert-template name="qpainter_drawlist"/>
</inject-code>
</add-function>
<modify-function signature="drawConvexPolygon(const QPointF*,int)" remove="all"/>
- <add-function signature="drawConvexPolygon(QList<QPointF>)">
+ <add-function signature="drawConvexPolygon(QList<QPointF>@points@)">
<inject-code>
<insert-template name="qpainter_drawlist"/>
</inject-code>
<!-- PYSIDE-1540: Preferably use the QPolygon overloads first to avoid
a costly sequence type check on QPolygon. -->
<modify-function signature="drawPoints(const QPoint*,int)" remove="all"/>
- <add-function signature="drawPoints(QList<QPoint>)" overload-number="2">
+ <add-function signature="drawPoints(QList<QPoint>@points@)" overload-number="2">
<inject-code>
<insert-template name="qpainter_drawlist"/>
</inject-code>
</add-function>
- <add-function signature="drawPoints(QList<QPointF>)" overload-number="3">
+ <add-function signature="drawPoints(QList<QPointF>@points@)" overload-number="3">
<inject-code>
<insert-template name="qpainter_drawlist"/>
</inject-code>
</add-function>
<modify-function signature="drawPolygon(const QPoint*,int,Qt::FillRule)" remove="all"/>
- <add-function signature="drawPolygon(QList<QPoint>,Qt::FillRule)">
+ <add-function signature="drawPolygon(QList<QPoint>@points@,Qt::FillRule@fill_rule@)">
<inject-code file="../glue/qtgui.cpp" snippet="qpainter-drawpolygon"/>
</add-function>
<modify-function signature="drawPolygon(const QPointF*,int,Qt::FillRule)" remove="all"/>
- <add-function signature="drawPolygon(QList<QPointF>,Qt::FillRule)">
+ <add-function signature="drawPolygon(QList<QPointF>@points@,Qt::FillRule@fill_rule@)">
<inject-code file="../glue/qtgui.cpp" snippet="qpainter-drawpolygon"/>
</add-function>
<modify-function signature="drawPolyline(const QPoint*,int)" remove="all"/>
- <add-function signature="drawPolyline(QList<QPoint>)">
+ <add-function signature="drawPolyline(QList<QPoint>@points@)">
<inject-code>
<insert-template name="qpainter_drawlist"/>
</inject-code>
<modify-function signature="drawPolyline(const QPointF*,int)" remove="all"/>
<modify-function signature="drawPolyline(const QPolygon&)" overload-number="0"/>
<modify-function signature="drawPolyline(const QPolygonF&)" overload-number="1"/>
- <add-function signature="drawPolyline(QList<QPointF>)">
+ <add-function signature="drawPolyline(QList<QPointF@points@>)">
<inject-code>
<insert-template name="qpainter_drawlist"/>
</inject-code>
<modify-function signature="QMatrix2x2(const float*)" remove="all"/>
<modify-function signature="copyDataTo(float*) const" remove="all"/>
<modify-function signature="constData()const" remove="all"/>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code_matrix">
<replace from="%MATRIX_SIZE" to="4"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code_matrix">
<replace from="%MATRIX_TYPE" to="float"/>
</insert-template>
</inject-code>
</add-function>
- <add-function signature="QMatrix2x2(PySequence*)">
+ <add-function signature="QMatrix2x2(PySequence*@elements@)">
<inject-code class="target" position="beginning">
<insert-template name="matrix_constructor">
<replace from="%SIZE" to="4"/>
<modify-function signature="QMatrix2x3(const float*)" remove="all"/>
<modify-function signature="copyDataTo(float*) const" remove="all"/>
<modify-function signature="constData()const" remove="all"/>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code_matrix">
<replace from="%MATRIX_SIZE" to="6"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code_matrix">
<replace from="%MATRIX_TYPE" to="float"/>
</insert-template>
</inject-code>
</add-function>
- <add-function signature="QMatrix2x3(PySequence*)">
+ <add-function signature="QMatrix2x3(PySequence*@elements@)">
<inject-code class="target" position="beginning">
<insert-template name="matrix_constructor">
<replace from="%SIZE" to="6"/>
<modify-function signature="QMatrix2x4(const float*)" remove="all"/>
<modify-function signature="copyDataTo(float*) const" remove="all"/>
<modify-function signature="constData()const" remove="all"/>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code_matrix">
<replace from="%MATRIX_SIZE" to="8"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code_matrix">
<replace from="%MATRIX_TYPE" to="float"/>
</insert-template>
</inject-code>
</add-function>
- <add-function signature="QMatrix2x4(PySequence*)">
+ <add-function signature="QMatrix2x4(PySequence*@elements@)">
<inject-code class="target" position="beginning">
<insert-template name="matrix_constructor">
<replace from="%SIZE" to="8"/>
<modify-function signature="QMatrix3x2(const float*)" remove="all"/>
<modify-function signature="copyDataTo(float*) const" remove="all"/>
<modify-function signature="constData()const" remove="all"/>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code_matrix">
<replace from="%MATRIX_SIZE" to="6"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code_matrix">
<replace from="%MATRIX_TYPE" to="float"/>
</insert-template>
</inject-code>
</add-function>
- <add-function signature="QMatrix3x2(PySequence*)">
+ <add-function signature="QMatrix3x2(PySequence*@elements@)">
<inject-code class="target" position="beginning">
<insert-template name="matrix_constructor">
<replace from="%SIZE" to="6"/>
<modify-function signature="QMatrix3x3(const float*)" remove="all"/>
<modify-function signature="copyDataTo(float*) const" remove="all"/>
<modify-function signature="constData()const" remove="all"/>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code_matrix">
<replace from="%MATRIX_SIZE" to="9"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code_matrix">
<replace from="%MATRIX_TYPE" to="float"/>
</insert-template>
</inject-code>
</add-function>
- <add-function signature="QMatrix3x3(PySequence*)">
+ <add-function signature="QMatrix3x3(PySequence*@elements@)">
<inject-code class="target" position="beginning">
<insert-template name="matrix_constructor">
<replace from="%SIZE" to="9"/>
<modify-function signature="QMatrix3x4(const float*)" remove="all"/>
<modify-function signature="copyDataTo(float*) const" remove="all"/>
<modify-function signature="constData()const" remove="all"/>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code_matrix">
<replace from="%MATRIX_SIZE" to="12"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code_matrix">
<replace from="%MATRIX_TYPE" to="float"/>
</insert-template>
</inject-code>
</add-function>
- <add-function signature="QMatrix3x4(PySequence*)">
+ <add-function signature="QMatrix3x4(PySequence*@elements@)">
<inject-code class="target" position="beginning">
<insert-template name="matrix_constructor">
<replace from="%SIZE" to="12"/>
<modify-function signature="QMatrix4x2(const float*)" remove="all"/>
<modify-function signature="copyDataTo(float*) const" remove="all"/>
<modify-function signature="constData()const" remove="all"/>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code_matrix">
<replace from="%MATRIX_SIZE" to="8"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code_matrix">
<replace from="%MATRIX_TYPE" to="float"/>
</insert-template>
</inject-code>
</add-function>
- <add-function signature="QMatrix4x2(PySequence*)">
+ <add-function signature="QMatrix4x2(PySequence*@elements@)">
<inject-code class="target" position="beginning">
<insert-template name="matrix_constructor">
<replace from="%SIZE" to="8"/>
<modify-function signature="QMatrix4x3(const float*)" remove="all"/>
<modify-function signature="copyDataTo(float*) const" remove="all"/>
<modify-function signature="constData()const" remove="all"/>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code_matrix">
<replace from="%MATRIX_SIZE" to="12"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code_matrix">
<replace from="%MATRIX_TYPE" to="float"/>
</insert-template>
</inject-code>
</add-function>
- <add-function signature="QMatrix4x3(PySequence*)">
+ <add-function signature="QMatrix4x3(PySequence*@elements@)">
<inject-code class="target" position="beginning">
<insert-template name="matrix_constructor">
<replace from="%SIZE" to="12"/>
<modify-function signature="__dummy(const QList<float > &)" remove="all"/>
<!-- that was the trick ^^^^^^^^^^^^^^^^^^^^^^ -->
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code_matrix">
<replace from="%MATRIX_SIZE" to="16"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code_matrix">
<replace from="%MATRIX_TYPE" to="float"/>
_______ end of matrix block _______ -->
<value-type name="QQuaternion" since="4.6">
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="%f, %f, %f, %f"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="dddd"/>
</value-type>
<object-type name="QTouchEvent" since="4.6">
- <add-function signature="__repr__" return-type="PyObject">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_qdebug_gui"/>
</inject-code>
</object-type>
<value-type name="QVector2D" since="4.6">
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="%f, %f"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="dd"/>
<extra-includes>
<include file-name="QMatrix4x4" location="global"/>
</extra-includes>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="%f, %f, %f"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="ddd"/>
<extra-includes>
<include file-name="QMatrix4x4" location="global"/>
</extra-includes>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="repr_code">
<replace from="%REPR_FORMAT" to="%f, %f, %f, %f"/>
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
<insert-template name="reduce_code">
<replace from="%REDUCE_FORMAT" to="dddd"/>
</extra-includes>
</primitive-type>
- <object-type name="QWindow" delete-in-main-thread="true">
+ <object-type name="QWindow" delete-in-main-thread="true" polymorphic-base="true">
<enum-type name="AncestorMode"/>
<enum-type name="Visibility"/>
<modify-function signature="raise()" rename="raise_"/>
<include file-name="qtguihelper.h" location="local"/>
</extra-includes>
<modify-function signature="QGuiApplication(int&,char**,int)" access="private"/>
- <add-function signature="QGuiApplication(QStringList)">
+ <add-function signature="QGuiApplication(QStringList@arguments@)">
<inject-code file="../glue/qtgui.cpp" snippet="qguiapplication-1"/>
</add-function>
<add-function signature="QGuiApplication()">
<value-type name="QPageLayout">
<enum-type name="Mode"/>
<enum-type name="Orientation"/>
+ <enum-type name="OutOfBoundsPolicy" since="6.8"/>
<enum-type name="Unit"/>
</value-type>
<value-type name="QPageRanges">
<enum-type name="TypeInterpretation"/>
<enum-type name="YUVLayout"/>
</value-type>
- <object-type name="QPdfWriter"/>
+ <value-type name="QPdfOutputIntent"/>
+ <object-type name="QPdfWriter">
+ <enum-type name="ColorModel" since="6.8"/>
+ </object-type>
<object-type name="QPointingDevice">
<enum-type name="PointerType" flags="PointerTypes"/>
<enum-type name="GrabTransition"/>
<object-type name="QRhiTexture" since="6.6" private="yes">
<enum-type name="Format"/>
<enum-type name="Flag" flags="Flags"/>
+ <value-type name="ViewFormat" private="yes" since="6.8"/>
</object-type>
<object-type name="QRhiSampler" since="6.6" private="yes">
<enum-type name="Filter"/>
${QtHelp_GEN_DIR}/qhelpindexwidget_wrapper.cpp
${QtHelp_GEN_DIR}/qhelplink_wrapper.cpp
${QtHelp_GEN_DIR}/qhelpsearchengine_wrapper.cpp
+${QtHelp_GEN_DIR}/qhelpsearchenginecore_wrapper.cpp
${QtHelp_GEN_DIR}/qhelpsearchquery_wrapper.cpp
${QtHelp_GEN_DIR}/qhelpsearchquerywidget_wrapper.cpp
${QtHelp_GEN_DIR}/qhelpsearchresult_wrapper.cpp
<load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
<value-type name="QCompressedHelpInfo"/>
- <value-type name="QHelpContentItem">
+ <object-type name="QHelpContentItem"> <!-- Non-copyable, non-movable in 6.8 -->
<modify-function signature="parent()const">
<modify-argument index="return">
<define-ownership owner="default"/>
</modify-argument>
</modify-function>
- </value-type>
+ </object-type>
<object-type name="QHelpContentModel" polymorphic-id-expression="qobject_cast<QHelpContentModel*>(%B)"/>
<object-type name="QHelpContentWidget"/>
<value-type name="QHelpGlobal"/>
<object-type name="QHelpEngine"/>
- <object-type name="QHelpEngineCore"/>
+ <object-type name="QHelpEngineCore">
+ <!-- Uses QFuture -->
+ <modify-function signature="requestIndexForCurrentFilter()const" remove="all"/>
+ <modify-function signature="requestIndex(QString)const" remove="all"/>
+ </object-type>
<value-type name="QHelpFilterData"/>
<object-type name="QHelpFilterEngine"/>
<object-type name="QHelpFilterSettingsWidget"/>
<object-type name="QHelpIndexModel"/>
<object-type name="QHelpIndexWidget"/>
<value-type name="QHelpLink"/>
+ <object-type name="QHelpSearchEngineCore"/>
<object-type name="QHelpSearchEngine"/>
<value-type name="QHelpSearchQuery">
<enum-type name="FieldName"/>
${QtHttpServer_GEN_DIR}/qhttpserverresponse_wrapper.cpp
${QtHttpServer_GEN_DIR}/qhttpserverrouter_wrapper.cpp
${QtHttpServer_GEN_DIR}/qhttpserverrouterrule_wrapper.cpp
+ ${QtHttpServer_GEN_DIR}/qhttpserverwebsocketupgraderesponse_wrapper.cpp
# module is always needed
${QtHttpServer_GEN_DIR}/qthttpserver_module_wrapper.cpp)
<inject-code class="target" position="beginning" file="../glue/qhttpserver.cpp"
snippet="qhttpserver-route"/>
</add-function>
- <add-function signature="afterRequest(PyCallable @callback@)">
+ <add-function signature="addAfterRequestHandler(QObject*@context@,PyCallable @callback@)">
<inject-code class="target" position="beginning" file="../glue/qhttpserver.cpp"
- snippet="qhttpserver-afterrequest"/>
+ snippet="qhttpserver-addafterrequesthandler"/>
</add-function>
</object-type>
<object-type name="QHttpServerResponder">
<object-type name="QHttpServerResponse"/>
<object-type name="QHttpServerRouter"/>
<object-type name="QHttpServerRouterRule"/>
+ <object-type name="QHttpServerWebSocketUpgradeResponse" since="6.8">
+ <enum-type name="ResponseType"/>
+ </object-type>
</typesystem>
${QtLocation_GEN_DIR}/qgeoroutereply_wrapper.cpp
${QtLocation_GEN_DIR}/qgeoroutesegment_wrapper.cpp
${QtLocation_GEN_DIR}/qgeoserviceprovider_wrapper.cpp
+${QtLocation_GEN_DIR}/qlocation_wrapper.cpp
${QtLocation_GEN_DIR}/qplace_wrapper.cpp
${QtLocation_GEN_DIR}/qplacecontentreply_wrapper.cpp
${QtLocation_GEN_DIR}/qplacedetailsreply_wrapper.cpp
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtPositioning/typesystem_positioning.xml" generate="no"/>
+ <namespace-type name="QLocation">
+ <enum-type name="Visibility" flags="VisibilityScope"/>
+ </namespace-type>
<object-type name="QGeoCodeReply">
<enum-type name="Error"/>
</object-type>
project(QtMultimedia)
set(QtMultimedia_SRC
+${QtMultimedia_GEN_DIR}/qabstractvideobuffer_wrapper.cpp
+${QtMultimedia_GEN_DIR}/qabstractvideobuffer_mapdata_wrapper.cpp
${QtMultimedia_GEN_DIR}/qaudiobuffer_wrapper.cpp
+${QtMultimedia_GEN_DIR}/qaudiobufferinput_wrapper.cpp
+${QtMultimedia_GEN_DIR}/qaudiobufferoutput_wrapper.cpp
${QtMultimedia_GEN_DIR}/qaudiodecoder_wrapper.cpp
${QtMultimedia_GEN_DIR}/qaudiodevice_wrapper.cpp
${QtMultimedia_GEN_DIR}/qaudioformat_wrapper.cpp
${QtMultimedia_GEN_DIR}/qscreencapture_wrapper.cpp
${QtMultimedia_GEN_DIR}/qsoundeffect_wrapper.cpp
${QtMultimedia_GEN_DIR}/qtvideo_wrapper.cpp
-${QtMultimedia_GEN_DIR}/qvideoframe_wrapper.cpp
${QtMultimedia_GEN_DIR}/qvideoframe_paintoptions_wrapper.cpp
+${QtMultimedia_GEN_DIR}/qvideoframe_wrapper.cpp
${QtMultimedia_GEN_DIR}/qvideoframeformat_wrapper.cpp
+${QtMultimedia_GEN_DIR}/qvideoframeinput_wrapper.cpp
${QtMultimedia_GEN_DIR}/qvideosink_wrapper.cpp
${QtMultimedia_GEN_DIR}/qwavedecoder_wrapper.cpp
${QtMultimedia_GEN_DIR}/qwindowcapture_wrapper.cpp
<namespace-type name="QtVideo" since="6.7">
<enum-type name="Rotation"/>
</namespace-type>
-
+ <object-type name="QAbstractVideoBuffer" since="6.8">
+ <value-type name="MapData"/>
+ </object-type>
<value-type name="QAudioBuffer">
<add-function signature="data()" return-type="PyBuffer">
<inject-code file="../glue/qtmultimedia.cpp" snippet="qaudiobuffer-data"/>
<inject-code file="../glue/qtmultimedia.cpp" snippet="qaudiobuffer-const-data"/>
</add-function>
</value-type>
+ <object-type name="QAudioBufferInput" since="6.8"/>
+ <object-type name="QAudioBufferOutput" since="6.8"/>
<object-type name="QAudioDecoder">
<enum-type name="Error"/>
</object-type>
<enum-type name="PlaybackState" since="6.1"/>
<enum-type name="Error"/>
<enum-type name="Loops" python-type="IntEnum" since="6.2.3"/>
+ <modify-function signature="setSource(QUrl)" allow-thread="true"/>
</object-type>
<!-- see qtmultimedia/5773f7214c7430a98dea3974c0597cb3ee0ea7f5 might reappear in 6.3
<object-type name="QMediaPlaylist"/>
<enum-type name="PixelFormat"/>
<enum-type name="YCbCrColorSpace"/>
</value-type>
+ <object-type name="QVideoFrameInput" since="6.8"/>
<object-type name="QWaveDecoder">
<!-- No implementation -->
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtMultimediaWidgets"
+<typesystem package="PySide6.QtMultimediaWidgets" doc-package="PySide6.QtMultimedia"
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
${QtNetwork_GEN_DIR}/qdnsmailexchangerecord_wrapper.cpp
${QtNetwork_GEN_DIR}/qdnsservicerecord_wrapper.cpp
${QtNetwork_GEN_DIR}/qdnstextrecord_wrapper.cpp
+${QtNetwork_GEN_DIR}/qdnstlsassociationrecord_wrapper.cpp
+${QtNetwork_GEN_DIR}/qformdatabuilder_wrapper.cpp
+${QtNetwork_GEN_DIR}/qformdatapartbuilder_wrapper.cpp
${QtNetwork_GEN_DIR}/qhostaddress_wrapper.cpp
${QtNetwork_GEN_DIR}/qhostinfo_wrapper.cpp
${QtNetwork_GEN_DIR}/qhstspolicy_wrapper.cpp
<value-type name="QDnsHostAddressRecord"/>
<object-type name="QDnsLookup">
<enum-type name="Error"/>
+ <enum-type name="Protocol" since="6.8"/>
<enum-type name="Type"/>
</object-type>
<value-type name="QDnsMailExchangeRecord"/>
<value-type name="QDnsServiceRecord"/>
<value-type name="QDnsTextRecord"/>
-
+ <value-type name="QDnsTlsAssociationRecord" since="6.8">
+ <enum-type name="CertificateUsage"/>
+ <enum-type name="Selector"/>
+ <enum-type name="MatchingType"/>
+ </value-type>
<object-type name="QDtls">
<enum-type name="HandshakeState"/>
<configuration condition="QT_CONFIG(dtls)"/>
</value-type>
</object-type>
+ <object-type name="QFormDataPartBuilder" since="6.8"/>
+ <object-type name="QFormDataBuilder" since="6.8">
+ <enum-type name="Option" flags="Options"/>
+ </object-type>
+
<value-type name="QHstsPolicy">
<enum-type name="PolicyFlag" flags="PolicyFlags"/>
</value-type>
<define-ownership class="target" owner="default"/>
</modify-argument>
</modify-function>
- <modify-function signature="get(const QNetworkRequest&)" allow-thread="yes"/>
- <modify-function signature="post(const QNetworkRequest &,QIODevice*)" allow-thread="yes"/>
- <modify-function signature="post(const QNetworkRequest &,const QByteArray &)" allow-thread="yes"/>
- <modify-function signature="put(const QNetworkRequest &,QIODevice*)" allow-thread="yes"/>
- <modify-function signature="put(const QNetworkRequest &,const QByteArray &)" allow-thread="yes"/>
- <modify-function signature="sendCustomRequest(const QNetworkRequest &,const QByteArray &,QIODevice*)" allow-thread="yes" since="4.7"/>
+ <modify-function signature="head(QNetworkRequest)" allow-thread="yes">
+ <modify-argument index="0"> <!-- Suppress return value heuristics -->
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="get(QNetworkRequest)" allow-thread="yes">
+ <modify-argument index="0"> <!-- Suppress return value heuristics -->
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="get(QNetworkRequest,QIODevice*)" allow-thread="yes">
+ <modify-argument index="0"> <!-- Suppress return value heuristics -->
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="get(QNetworkRequest,QByteArray)" allow-thread="yes">
+ <modify-argument index="0"> <!-- Suppress return value heuristics -->
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="post(QNetworkRequest,QIODevice*)" allow-thread="yes">
+ <modify-argument index="0"> <!-- Suppress return value heuristics -->
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="post(QNetworkRequest,QByteArray)" allow-thread="yes">
+ <modify-argument index="0"> <!-- Suppress return value heuristics -->
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="put(QNetworkRequest,QIODevice*)" allow-thread="yes">
+ <modify-argument index="0"> <!-- Suppress return value heuristics -->
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="put(QNetworkRequest,QByteArray)" allow-thread="yes">
+ <modify-argument index="0"> <!-- Suppress return value heuristics -->
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="sendCustomRequest(QNetworkRequest,QByteArray,QIODevice*)" allow-thread="yes">
+ <modify-argument index="0"> <!-- Suppress return value heuristics -->
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="sendCustomRequest(QNetworkRequest,QByteArray,QByteArray)" allow-thread="yes">
+ <modify-argument index="0"> <!-- Suppress return value heuristics -->
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ </modify-function>
<modify-function signature="setCache(QAbstractNetworkCache*)">
<modify-argument index="1">
<define-ownership class="target" owner="c++"/>
<inject-code class="native" position="beginning" file="../glue/qtnetwork.cpp"
snippet="qhostinfo-lookuphost-functor"/>
<enum-type name="HostInfoError"/>
- <add-function signature="lookupHost(const QString &,PyCallable)">
+ <add-function signature="lookupHost(const QString &@name@,PyCallable@callable@)">
<inject-code class="target" position="beginning"
file="../glue/qtnetwork.cpp"
snippet="qhostinfo-lookuphost-callable"/>
</value-type>
<value-type name="QIPv6Address">
- <add-function signature="__len__">
+ <add-function signature="__len__" return-type="int">
<inject-code class="target" position="beginning" file="../glue/qtnetwork.cpp" snippet="qipv6address-len"/>
</add-function>
<add-function signature="__getitem__">
<!-- The above entries may be present in the system or not. Keep this section organized. -->
</typesystem>
-
${QtNetworkAuth_GEN_DIR}/qoauth2authorizationcodeflow_wrapper.cpp
${QtNetworkAuth_GEN_DIR}/qoauthhttpserverreplyhandler_wrapper.cpp
${QtNetworkAuth_GEN_DIR}/qoauthoobreplyhandler_wrapper.cpp
+${QtNetworkAuth_GEN_DIR}/qoauthurischemereplyhandler_wrapper.cpp
# module is always needed
${QtNetworkAuth_GEN_DIR}/qtnetworkauth_module_wrapper.cpp
)
<value-type name="QOAuth1Signature">
<enum-type name="HttpRequestMethod"/>
</value-type>
- <object-type name="QOAuth2AuthorizationCodeFlow"/>
+ <object-type name="QOAuth2AuthorizationCodeFlow">
+ <enum-type name="PkceMethod" since="6.8"/>
+ </object-type>
<object-type name="QOAuthHttpServerReplyHandler"/>
<object-type name="QOAuthOobReplyHandler"/>
+ <object-type name="QOAuthUriSchemeReplyHandler" since="6.8"/>
<suppress-warning text="^.*Typedef used on signal QAbstractOAuth.*$"/>
</typesystem>
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtOpenGLWidgets"
+<typesystem package="PySide6.QtOpenGLWidgets" doc-package="PySide6.QtOpenGL"
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no" />
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no" />
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
<typesystem package="PySide6.QtPdfWidgets"
- namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE"
+ doc-package="PySide6.QtPdf">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
<!-- fixme: Check if this is still required in Qt 6:
bool QPagedPaintDevice::setPageSize(QPageSize)
void QPagedPaintDevice::setPageSize(QPagedPaintDevice::PageSize) -->
- <add-function signature="setPageSize(const QPageSize&)" return-type="bool">
+ <add-function signature="setPageSize(const QPageSize&@size@)" return-type="bool">
<inject-code file="../glue/qtprintsupport.cpp" snippet="setpagesize" />
</add-function>
</object-type>
</object-type>
</typesystem>
-
#include <pep384ext.h>
#include <signature.h>
+#include <sbktypefactory.h>
#include <QtCore/QDebug>
<load-typesystem name="QtNetwork/typesystem_network.xml" generate="no"/>
<rejection class="QQmlPrivate"/>
+ <rejection class="*" enum-name="QmlIsAnonymous"/>
+ <rejection class="*" enum-name="QmlIsUncreatable"/>
+ <rejection class="*" enum-name="QmlIsSingleton"/>
+ <rejection class="*" enum-name="QmlIsInterface"/>
+ <rejection class="*" enum-name="QmlIsSequence"/>
<inject-code class="target" position="declaration">
// Volatile Bool Ptr type definition for QQmlIncubationController::incubateWhile(std::atomic<bool> *, int)
<add-function signature="qmlRegisterType(PyTypeObject@type_obj@,const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@)" return-type="int">
<inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistertype"/>
- <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+ <inject-documentation format="target" mode="append" file="../doc/qtqml.rst"
snippet="qmlregistertype"/>
<modify-argument index="2" pyi-type="str"/>
</add-function>
<add-function signature="qmlRegisterSingletonType(PyTypeObject@type_obj@,const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@,PyObject*@callback@)" return-type="int">
<inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistersingletontype_qobject_callback"/>
- <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+ <inject-documentation format="target" mode="append" file="../doc/qtqml.rst"
snippet="qmlregistersingletontype_qobject_callback"/>
<modify-argument index="2" pyi-type="str"/>
</add-function>
<add-function signature="qmlRegisterSingletonType(PyTypeObject@type_obj@,const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@)" return-type="int">
<inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistersingletontype_qobject_nocallback"/>
- <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+ <inject-documentation format="target" mode="append" file="../doc/qtqml.rst"
snippet="qmlregistersingletontype_qobject_nocallback"/>
<modify-argument index="2" pyi-type="str"/>
</add-function>
<add-function signature="qmlRegisterSingletonType(const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@,PyObject*@callback@)" return-type="int">
<inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistersingletontype_qjsvalue"/>
- <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+ <inject-documentation format="target" mode="append" file="../doc/qtqml.rst"
snippet="qmlregistersingletontype_qjsvalue"/>
<modify-argument index="1" pyi-type="str"/>
</add-function>
<add-function signature="qmlRegisterSingletonInstance(PyTypeObject@type_obj@,const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@,PyObject*@callback@)" return-type="int">
<inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregistersingletoninstance"/>
- <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+ <inject-documentation format="target" mode="append" file="../doc/qtqml.rst"
snippet="qmlregistersingletoninstance"/>
<modify-argument index="2" pyi-type="str"/>
</add-function>
<add-function signature="qmlRegisterUncreatableType(PyTypeObject@type_obj@,const char*@uri@,int@version_major@,int@version_minor@,const char*@qml_name@,const char*@message@)" return-type="int">
<inject-code class="target" file="../glue/qtqml.cpp" snippet="qmlregisteruncreatabletype"/>
- <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+ <inject-documentation format="target" mode="append" file="../doc/qtqml.rst"
snippet="qmlregisteruncreatabletype"/>
<modify-argument index="2" pyi-type="str"/>
</add-function>
<object-type name="QJSEngine">
<enum-type name="Extension" flags="Extensions"/>
<enum-type name="ObjectOwnership"/>
- <add-function signature="toScriptValue(const QVariant&)" return-type="QJSValue">
+ <add-function signature="toScriptValue(const QVariant&@value@)" return-type="QJSValue">
<inject-code class="target" position="end" file="../glue/qtqml.cpp" snippet="qjsengine-toscriptvalue"/>
</add-function>
</object-type>
<value-type name="PropertyPair"/>
</object-type>
<value-type name="QQmlError">
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning" file="../glue/qtqml.cpp" snippet="qmlerrror-repr"/>
</add-function>
</value-type>
</modify-argument>
<inject-code class="target" file="../glue/qtqml.cpp"
snippet="qqmlengine-singletoninstance-qmltypeid"/>
- <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+ <inject-documentation format="target" mode="append" file="../doc/qtqml.rst"
snippet="qqmlengine-singletoninstance-qmltypeid"/>
</add-function>
<add-function signature="singletonInstance(QString@uri@,QString@typeName@)"
</modify-argument>
<inject-code class="target" file="../glue/qtqml.cpp"
snippet="qqmlengine-singletoninstance-typename"/>
- <inject-documentation format="target" mode="append" file="../doc/qtqml_functions.rst"
+ <inject-documentation format="target" mode="append" file="../doc/qtqml.rst"
snippet="qqmlengine-singletoninstance-typename"/>
</add-function>
</object-type>
if (Qt${QT_MAJOR_VERSION}OpenGL_FOUND)
list(APPEND QtQuick_SRC
${QtQuick_GEN_DIR}/qquickframebufferobject_wrapper.cpp
- ${QtQuick_GEN_DIR}/qquickframebufferobject_renderer_wrapper.cpp)
+ ${QtQuick_GEN_DIR}/qquickframebufferobject_renderer_wrapper.cpp
+ ${QtQuick_GEN_DIR}/qquickopenglutils_wrapper.cpp)
else()
- list(APPEND QtQuick_DROPPED_ENTRIES QQuickFramebufferObject)
+ list(APPEND QtQuick_DROPPED_ENTRIES QQuickFramebufferObject QQuickOpenGLUtils)
endif()
create_pyside_module(NAME QtQuick
</modify-argument>
</modify-function>
</object-type>
+ <namespace-type name="QQuickOpenGLUtils"/>
<value-type name="QQuickGraphicsConfiguration"/>
<value-type name="QQuickGraphicsDevice">
<object-type name="QQuickImageResponse"/>
<object-type name="QQuickTransform"/>
- <object-type name="QQuickItem" delete-in-main-thread="true">
+ <object-type name="QQuickItem" delete-in-main-thread="true" polymorphic-base="true">
<value-type name="UpdatePaintNodeData"/>
<enum-type name="Flag" flags="Flags"/>
<enum-type name="ItemChange"/>
<!-- PYSIDE-1726, after qtbase/696d94b132b2f352b5e6b889ad91c2437417fae8,
the function is defined with dummy types when Vulkan is not present. -->
<modify-function signature="^fromVulkanImage\(.*$" remove="all"/>
+ <enum-type name="Flag" flags="Flags" since="6.8"/>
</value-type>
<object-type name="QQuickTextDocument">
</value-type>
<value-type name="GraphicsPipelineState">
<enum-type name="BlendFactor"/>
+ <enum-type name="BlendOp" since="6.8"/>
<enum-type name="ColorMaskComponent" flags="ColorMask"/>
<enum-type name="CullMode"/>
<enum-type name="PolygonMode"/>
</object-type>
<object-type name="QQuick3DInstancing">
<value-type name="InstanceTableEntry"/>
+ <add-function signature="getInstanceBufferOverride()"
+ return-type="std::pair<QByteArray,int>" python-override="true"/>
+ <modify-function signature="getInstanceBuffer(int*)">
+ <inject-code class="shell" position="override" file="../glue/qtquick3d.cpp"
+ snippet="qquick3dinstancing-getinstancebuffer-virtual-redirect"/>
+ <modify-argument index="return" pyi-type="Tuple[bool, str]"/>
+ <modify-argument index="1"><remove-default-expression/><remove-argument/></modify-argument>
+ <inject-code class="target" position="beginning" file="../glue/qtquick3d.cpp"
+ snippet="qquick3dinstancing-getinstancebuffer-return"/>
+ </modify-function>
</object-type>
<object-type name="QQuick3DTextureData">
<enum-type name="Format"/>
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtQuickControls2"
+<typesystem package="PySide6.QtQuickControls2" doc-package="PySide6.QtQuickControls"
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtQuick/typesystem_quick.xml" generate="no"/>
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtQuickWidgets"
+<typesystem package="PySide6.QtQuickWidgets" doc-package="PySide6.QtQuick"
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<object-type name="QCanBus">
<!-- Remove errorMessage argument, return tuple instead. -->
- <modify-function signature="availableDevices(QString,QString*)const">
- <modify-argument index="2">
+ <modify-function signature="availableDevices(QString*)const">
+ <modify-argument index="1">
<remove-default-expression/>
<remove-argument/>
</modify-argument>
The function returns a tuple of (device_list, error_string).
</inject-documentation>
</modify-function>
+ <!-- Remove errorMessage argument, return tuple instead. -->
+ <modify-function signature="availableDevices(QString,QString*)const">
+ <modify-argument index="2">
+ <remove-default-expression/>
+ <remove-argument/>
+ </modify-argument>
+ <modify-argument index="return">
+ <replace-type modified-type="tuple"/>
+ </modify-argument>
+ <inject-code class="target" position="beginning"
+ file="../glue/qtserialbus.cpp" snippet="qcanbus-available-devices-plugin"/>
+ <inject-documentation format="target" mode="append" emphasis="language-note">
+ The function returns a tuple of (device_list, error_string).
+ </inject-documentation>
+ </modify-function>
+
<modify-function signature="createDevice(QString,QString,QString*)const">
<modify-argument index="3">
<remove-default-expression/>
</modify-argument>
<inject-code class="target" position="beginning"
file="../glue/qtserialbus.cpp" snippet="qcanbus-createdevice"/>
- <inject-documentation format="target" mode="append">
+ <inject-documentation format="target" mode="append" emphasis="language-note">
The function returns a tuple of (device, error_string).
</inject-documentation>
</modify-function>
<value-type name="MultiplexValueRange"/>
</value-type>
<value-type name="QCanUniqueIdDescription"/>
- <object-type name="QModbusClient"/>
+ <object-type name="QModbusClient">
+ <modify-function signature="sendReadRequest(QModbusDataUnit,int)">
+ <modify-argument index="0"> <!-- Suppress return value heuristics -->
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="sendWriteRequest(QModbusDataUnit,int)">
+ <modify-argument index="0"> <!-- Suppress return value heuristics -->
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="sendReadWriteRequest(QModbusDataUnit,QModbusDataUnit,int)">
+ <modify-argument index="0"> <!-- Suppress return value heuristics -->
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="sendRawRequest(QModbusRequest,int)">
+ <modify-argument index="0"> <!-- Suppress return value heuristics -->
+ <define-ownership class="target" owner="default"/>
+ </modify-argument>
+ </modify-function>
+ </object-type>
<value-type name="QModbusDataUnit">
<enum-type name="RegisterType"/>
</value-type>
<load-typesystem name="QtMultimedia/typesystem_multimedia.xml" generate="no"/>
<object-type name="QAmbientSound">
- <enum-type name="Loops"/>
+ <enum-type name="Loops" python-type="IntEnum"/>
</object-type>
<object-type name="QAudioEngine">
</object-type>
<object-type name="QSpatialSound">
- <enum-type name="Loops"/>
+ <enum-type name="Loops" python-type="IntEnum"/>
<enum-type name="DistanceModel"/>
</object-type>
</typesystem>
</object-type>
<object-type name="QSignalTransition">
- <add-function signature="QSignalTransition(PyObject*,QState*)" return-type="QSignalTransition*">
+ <add-function signature="QSignalTransition(PyObject*@signal@,QState*@state@)">
<modify-argument index="2">
<replace-default-expression with="0"/>
</modify-argument>
<!-- FIXME: the proper signature for this added function would be something like
addTransition(PySide2.QtCore.Signal, QAbstractState*)
but that depends on bug #362. -->
- <add-function signature="addTransition(PyObject*,QAbstractState*)" return-type="QSignalTransition*">
+ <add-function signature="addTransition(PyObject*@signal@,QAbstractState*)" return-type="QSignalTransition*">
<modify-argument index="return">
<parent index="this" action="add"/>
</modify-argument>
</modify-argument>
</modify-function>
+ <modify-function signature="postEvent(QEvent*,QStateMachine::EventPriority)">
+ <modify-argument index="1">
+ <define-ownership owner="c++"/>
+ </modify-argument>
+ </modify-function>
+ <modify-function signature="postDelayedEvent(QEvent*,int)">
+ <modify-argument index="1">
+ <define-ownership owner="c++"/>
+ </modify-argument>
+ </modify-function>
+
<add-function signature="configuration()" return-type="QSet<QAbstractState*>">
<inject-code class="target" position="beginning" file="../glue/qtstatemachine.cpp"
snippet="qstatemachine-configuration"/>
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtSvgWidgets"
+<typesystem package="PySide6.QtSvgWidgets" doc-package="PySide6.QtSvg"
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtSvg/typesystem_svg.xml" generate="no"/>
<load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtTest"
+<typesystem package="PySide6.QtTest" doc-package="PySide6.QtTestlib"
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
project(QtWebEngineCore)
+set(QtWebEngineCore_static_sources
+ "${QtWebEngineCore_SOURCE_DIR}/glue/webenginepage_functors.cpp"
+ "${QtWebEngineCore_SOURCE_DIR}/glue/webenginepage_functors.h"
+ "${QtWebEngineCore_SOURCE_DIR}/glue/webengineframe.cpp"
+ "${QtWebEngineCore_SOURCE_DIR}/glue/webengineframe.h"
+)
+
set(QtWebEngineCore_SRC
${QtWebEngineCore_GEN_DIR}/qwebenginecertificateerror_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebengineclientcertificateselection_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebengineclientcertificatestore_wrapper.cpp
+${QtWebEngineCore_GEN_DIR}/qwebengineclienthints_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginecontextmenurequest_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginecookiestore_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginecookiestore_filterrequest_wrapper.cpp
-# FIXME ${QtWebEngineCore_GEN_DIR}/qwebenginedesktopmediarequest_wrapper.cpp
+${QtWebEngineCore_GEN_DIR}/qwebenginedesktopmediarequest_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginedownloadrequest_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginefilesystemaccessrequest_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginefindtextresult_wrapper.cpp
+${QtWebEngineCore_GEN_DIR}/qwebengineframe_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginefullscreenrequest_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebengineglobalsettings_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebengineglobalsettings_dnsmode_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginehttprequest_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginenavigationrequest_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebengineloadinginfo_wrapper.cpp
-# FIXME ${QtWebEngineCore_GEN_DIR}/qwebenginemediasourcemodel_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginenewwindowrequest_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginenotification_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginepage_wrapper.cpp
+${QtWebEngineCore_GEN_DIR}/qwebenginepermission_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebengineprofile_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebenginequotarequest_wrapper.cpp
${QtWebEngineCore_GEN_DIR}/qwebengineregisterprotocolhandlerrequest_wrapper.cpp
create_pyside_module(NAME QtWebEngineCore
INCLUDE_DIRS QtWebEngineCore_include_dirs
+ STATIC_SOURCES QtWebEngineCore_static_sources
LIBRARIES QtWebEngineCore_libraries
DEPS QtWebEngineCore_deps
TYPESYSTEM_PATH QtWebEngineCore_SOURCE_DIR
SOURCES QtWebEngineCore_SRC)
-
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "webengineframe.h"
+
+#include <QtWebEngineCore/qwebengineframe.h>
+
+QT_BEGIN_NAMESPACE
+
+// Enable using QWebEngineFrame as a value-type by adding a way of
+// default-constructing by creating a replica with the same data members.
+// (see attribute "default-constructor").
+QWebEngineFrame defaultConstructedWebEngineFrame()
+{
+ class FriendlyWebEngineFrame // Keep in sync with QWebEngineFrame
+ {
+ public:
+ QWeakPointer<QObject> m_w;
+ quint64 m_id = 0;
+ };
+
+ FriendlyWebEngineFrame frame;
+ return std::move(*reinterpret_cast<QWebEngineFrame*>(&frame));
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef WEBENGINEFRAME_H
+#define WEBENGINEFRAME_H
+
+#include <QtCore/QtClassHelperMacros>
+
+QT_FORWARD_DECLARE_CLASS(QWebEngineFrame)
+
+QT_BEGIN_NAMESPACE
+
+QWebEngineFrame defaultConstructedWebEngineFrame();
+
+QT_END_NAMESPACE
+
+#endif // WEBENGINEFRAME_H
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "webenginepage_functors.h"
+
+#include "autodecref.h"
+#include "gilstate.h"
+#include "sbkconverter.h"
+
+#include "pysideutils.h"
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+void RunJavascriptFunctor::operator()(const QVariant &result)
+{
+ Shiboken::GilState state;
+ Shiboken::AutoDecRef arglist(PyTuple_New(1));
+ switch (result.typeId()) {
+ case QMetaType::Bool: {
+ PyObject *pyValue = result.toBool() ? Py_True : Py_False;
+ Py_INCREF(pyValue);
+ PyTuple_SetItem(arglist, 0, pyValue);
+ }
+ break;
+ case QMetaType::Int:
+ case QMetaType::UInt:
+ case QMetaType::LongLong:
+ case QMetaType::ULongLong:
+ case QMetaType::Double:
+ PyTuple_SetItem(arglist, 0, PyFloat_FromDouble(result.toDouble()));
+ break;
+ default: {
+ const QString value = result.toString();
+ PyTuple_SetItem(arglist, 0, PySide::qStringToPyUnicode(value));
+ }
+ break;
+ }
+ Shiboken::AutoDecRef ret(PyObject_CallObject(object(), arglist));
+ release(); // single shot
+}
+
+void PrintToPdfFunctor::operator()(const QByteArray &pdf)
+{
+ Shiboken::GilState state;
+ Shiboken::AutoDecRef arglist(PyTuple_New(1));
+
+ Shiboken::Conversions::SpecificConverter converter("QByteArray");
+ PyObject *pyPdf = converter.toPython(&pdf);
+ PyTuple_SetItem(arglist, 0, pyPdf);
+ Shiboken::AutoDecRef ret(PyObject_CallObject(object(), arglist));
+ release(); // single shot
+}
+
+QT_END_NAMESPACE
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef WEBENGINEPAGE_FUNCTORS_H
+#define WEBENGINEPAGE_FUNCTORS_H
+
+#include "pyobjectholder.h"
+
+#include <QtCore/QtClassHelperMacros>
+
+QT_FORWARD_DECLARE_CLASS(QByteArray)
+QT_FORWARD_DECLARE_CLASS(QVariant)
+
+QT_BEGIN_NAMESPACE
+
+struct RunJavascriptFunctor : public Shiboken::PyObjectHolder
+{
+ using Shiboken::PyObjectHolder::PyObjectHolder;
+
+ void operator()(const QVariant &result);
+};
+
+struct PrintToPdfFunctor : public Shiboken::PyObjectHolder
+{
+ using Shiboken::PyObjectHolder::PyObjectHolder;
+
+ void operator()(const QByteArray &pdf);
+};
+
+QT_END_NAMESPACE
+
+#endif // WEBENGINEPAGE_FUNCTORS_H
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtWebEngineCore"
+<typesystem package="PySide6.QtWebEngineCore" doc-package="PySide6.QtWebEngine"
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<load-typesystem name="QtPrintSupport/typesystem_printsupport.xml" generate="no"/>
<load-typesystem name="QtWebChannel/typesystem_webchannel.xml" generate="no"/>
+ <extra-includes>
+ <include file-name="glue/webengineframe.h" location="global"/>
+ </extra-includes>
+
<rejection class="extensions"/>
<function signature="qWebEngineChromiumVersion()"/>
<enum-type name="Type"/>
</value-type>
+ <object-type name="QWebEngineClientHints" since="6.8"/>
+
<object-type name="QWebEngineContextMenuRequest">
<enum-type name="EditFlag" flags="EditFlags"/>
<enum-type name="MediaFlag" flags="MediaFlags"/>
<modify-function signature="swap(QWebEngineFileSystemAccessRequest&)" remove="all"/>
</value-type>
+ <value-type name="QWebEngineFrame" default-constructor="defaultConstructedWebEngineFrame()"
+ since="6.8">
+ <extra-includes>
+ <include file-name="glue/webenginepage_functors.h" location="global"/>
+ <include file-name="glue/webengineframe.h" location="global"/>
+ </extra-includes>
+ <modify-function signature="^runJavaScript\(.*\)$" remove="yes"/>
+ <add-function signature="runJavaScript(const QString &@scriptSource@,PyCallable*@resultCallback@)">
+ <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+ snippet="qwebenginepage-runjavascript-2"/>
+ </add-function>
+ <add-function signature="runJavaScript(const QString &@scriptSource@,quint32@worldId@,PyCallable*@resultCallback@={})">
+ <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+ snippet="qwebenginepage-runjavascript-3"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+ snippet="qwebenginepage-runjavascript"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+ snippet="qwebenginepage-async-note"/>
+ </add-function>
+ <add-function signature="printToPdf(PyCallable*@resultCallback@)">
+ <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+ snippet="qwebengineframe-printtopdf"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+ snippet="qwebengineframe-printtopdf"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+ snippet="qwebenginepage-async-note"/>
+ </add-function>
+ </value-type>
+
<value-type name="QWebEngineFullScreenRequest"/>
<object-type name="QWebEngineHistory"/>
<object-type name="QWebEngineNotification"/>
<object-type name="QWebEnginePage">
+ <extra-includes>
+ <include file-name="glue/webenginepage_functors.h" location="global"/>
+ </extra-includes>
<enum-type name="LifecycleState"/>
<enum-type name="WebAction"/>
<enum-type name="FindFlag" flags="FindFlags"/>
<inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
snippet="qwebenginepage-javascriptprompt-return"/>
</modify-function>
- <add-function signature="findText(const QString &,QWebEnginePage::FindFlags,PyObject*)">
- <inject-code class="target" position="beginning" file="../glue/qtwebenginewidgets.cpp" snippet="qwebenginepage-findtext"/>
+ <add-function signature="findFrameByName(QString@name@)" return-type="QWebEngineFrame">
+ <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+ snippet="qwebenginepage-findframebyname"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+ snippet="qwebenginepage-findframebyname"/>
</add-function>
- <add-function signature="toPlainText(PyObject*) const">
- <inject-code class="target" position="beginning" file="../glue/qtwebenginewidgets.cpp" snippet="qwebenginepage-convertto"/>
+ <add-function signature="findText(const QString &@subString@,QWebEnginePage::FindFlags@options@,PyCallable*@resultCallback@)">
+ <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+ snippet="qwebenginepage-findtext"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+ snippet="qwebenginepage-findtext"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+ snippet="qwebenginepage-async-note"/>
</add-function>
- <add-function signature="toHtml(PyObject*) const">
- <inject-code class="target" position="beginning" file="../glue/qtwebenginewidgets.cpp" snippet="qwebenginepage-convertto"/>
+ <add-function signature="toPlainText(PyCallable*@resultCallback@) const">
+ <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+ snippet="qwebenginepage-convertto"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+ snippet="qwebenginepage-toplaintext"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+ snippet="qwebenginepage-async-note"/>
</add-function>
- <add-function signature="runJavaScript(const QString &,quint32,PyObject*)">
- <inject-code class="target" position="beginning" file="../glue/qtwebenginewidgets.cpp" snippet="qwebenginepage-runjavascript"/>
+ <add-function signature="toHtml(PyCallable*@resultCallback@) const">
+ <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+ snippet="qwebenginepage-convertto"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+ snippet="qwebenginepage-tohtml"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+ snippet="qwebenginepage-async-note"/>
+ </add-function>
+ <modify-function signature="^runJavaScript\(.*\)$" remove="yes"/>
+ <add-function signature="runJavaScript(const QString &@scriptSource@,PyCallable*@resultCallback@)">
+ <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+ snippet="qwebenginepage-runjavascript-2"/>
+ </add-function>
+ <add-function signature="runJavaScript(const QString &@scriptSource@,quint32@worldId@=0,PyCallable*@resultCallback@={})">
+ <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+ snippet="qwebenginepage-runjavascript-3"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+ snippet="qwebenginepage-runjavascript"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+ snippet="qwebenginepage-async-note"/>
+ </add-function>
+ <add-function signature="printToPdf(PyCallable*@resultCallback@,const QPageLayout &@pageLayout@=QPageLayout(QPageSize(QPageSize::A4),QPageLayout::Portrait,QMarginsF()),const QPageRanges &@ranges@={})">
+ <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+ snippet="qwebenginepage-printtopdf"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+ snippet="qwebenginepage-printtopdf"/>
+ <inject-documentation format="target" mode="append" file="../doc/qtwebenginecore.rst"
+ snippet="qwebenginepage-async-note"/>
</add-function>
</object-type>
+ <value-type name="QWebEnginePermission" since="6.8">
+ <enum-type name="PermissionType"/>
+ <enum-type name="State"/>
+ </value-type>
+
<object-type name="QWebEngineProfile">
<extra-includes>
<include file-name="QtWebEngineCore/QWebEngineNotification" location="global"/>
snippet="qwebengineprofile-functor"/>
<enum-type name="HttpCacheType"/>
<enum-type name="PersistentCookiesPolicy"/>
+ <enum-type name="PersistentPermissionsPolicy" since="6.8"/>
<add-function signature="setNotificationPresenter(PyCallable* @notificationPresenter@)">
<inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp" snippet="qwebengineprofile-setnotificationpresenter"/>
</add-function>
<object-type name="QWebEngineSettings">
<enum-type name="FontFamily"/>
<enum-type name="FontSize"/>
+ <enum-type name="ImageAnimationPolicy" since="6.8"/>
<enum-type name="UnknownUrlSchemePolicy"/>
<enum-type name="WebAttribute"/>
+
</object-type>
<object-type name="QWebEngineHttpRequest">
<object-type name="QWebEngineUrlSchemeHandler"/>
- <!-- FIXME not in snapshot yet
- <value-type name="QWebEngineDesktopMediaRequest" since="6.7"/>
- <object-type name="QWebEngineMediaSourceModel" since="6.7"/>
- -->
+ <!-- No default constructor. Used in signal QWebEnginePage.desktopMediaRequested(),
+ but not in any other function. -->
+ <value-type name="QWebEngineDesktopMediaRequest" since="6.7">
+ <modify-function signature="swap(QWebEngineDesktopMediaRequest&)" remove="yes"/>
+ </value-type>
<value-type name="QWebEngineWebAuthPinRequest" since="6.7"/>
<object-type name="QWebEngineWebAuthUxRequest" since="6.7">
<enum-type name="WebAuthUxState"/>
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtWebEngineQuick"
+<typesystem package="PySide6.QtWebEngineQuick" doc-package="PySide6.QtWebEngine"
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtQml/typesystem_qml.xml" generate="no"/>
<load-typesystem name="QtWebEngineCore/typesystem_webenginecore.xml" generate="no"/>
<object-type name="QQuickWebEngineProfile">
<enum-type name="HttpCacheType"/>
<enum-type name="PersistentCookiesPolicy"/>
+ <enum-type name="PersistentPermissionsPolicy" since="6.8"/>
</object-type>
</typesystem>
DEPS QtWebEngineWidgets_deps
TYPESYSTEM_PATH QtWebEngineWidgets_SOURCE_DIR
SOURCES QtWebEngineWidgets_SRC)
-
-
-
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-->
-<typesystem package="PySide6.QtWebEngineWidgets"
+<typesystem package="PySide6.QtWebEngineWidgets" doc-package="PySide6.QtWebEngine"
namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
<load-typesystem name="QtCore/typesystem_core.xml" generate="no"/>
<load-typesystem name="QtGui/typesystem_gui.xml" generate="no"/>
<load-typesystem name="QtPrintSupport/typesystem_printsupport.xml" generate="no"/>
<object-type name="QWebEngineView">
- <add-function signature="findText(const QString &,QWebEnginePage::FindFlags,PyObject*)">
- <inject-code class="target" position="beginning" file="../glue/qtwebenginewidgets.cpp" snippet="qwebenginepage-findtext"/>
+ <add-function signature="findText(const QString &@subString@,QWebEnginePage::FindFlags@options@,PyCallable*@resultCallback@)">
+ <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp"
+ snippet="qwebenginepage-findtext"/>
</add-function>
</object-type>
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+project(QtWebView)
+
+set(QtWebView_Src
+${QtWebView_GEN_DIR}/qtwebview_wrapper.cpp
+# module is always needed
+${QtWebView_GEN_DIR}/qtwebview_module_wrapper.cpp
+)
+
+set(QtWebView_include_dirs ${QtWebView_SOURCE_DIR}
+ ${QtWebView_BINARY_DIR}
+ ${Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}Gui_INCLUDE_DIRS}
+ ${Qt${QT_MAJOR_VERSION}WebView_INCLUDE_DIRS}
+ ${libpyside_SOURCE_DIR}
+ ${QtGui_GEN_DIR}
+ ${QtCore_GEN_DIR}
+ ${QtWebView_GEN_DIR})
+
+set(QtWebView_libraries pyside6
+ ${Qt${QT_MAJOR_VERSION}WebView_LIBRARIES})
+
+set(QtWebView_deps QtGui)
+
+# for Windows and Linux, QtWebView depends on QtWebEngine to render content
+if ((WIN32 OR UNIX) AND NOT APPLE)
+ list(APPEND QtWebView_deps QtWebEngineCore QtWebEngineQuick)
+endif()
+
+create_pyside_module(NAME QtWebView
+ INCLUDE_DIRS QtWebView_include_dirs
+ LIBRARIES QtWebView_libraries
+ DEPS QtWebView_deps
+ TYPESYSTEM_PATH QtWebView_SOURCE_DIR
+ SOURCES QtWebView_Src)
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+-->
+<typesystem package="PySide6.QtWebView"
+ namespace-begin="QT_BEGIN_NAMESPACE" namespace-end="QT_END_NAMESPACE">
+ <load-typesystem name="QtCore/typesystem_core.xml" generate ="no"/>
+ <namespace-type name="QtWebView"/>
+</typesystem>
</modify-function>
</object-type>
<object-type name="QAbstractButton">
- <add-function signature="setShortcut(Qt::Key)">
+ <add-function signature="setShortcut(Qt::Key@key@)">
<inject-code file="../glue/qtgui.cpp" snippet="set-qtkey-shortcut"/>
</add-function>
</object-type>
<modify-function signature="getColor(const QColor&,QWidget*,const QString&,QFlags<QColorDialog::ColorDialogOption>)" allow-thread="yes"/>
</object-type>
- <object-type name="QLayout">
+ <object-type name="QLayout" polymorphic-base="true">
<inject-code class="native" position="beginning" file="../glue/qtwidgets.cpp"
snippet="qwidget-retrieveobjectname"/>
<inject-code class="native" position="beginning" file="../glue/qtwidgets.cpp" snippet="qlayout-help-functions"/>
</inject-code>
</modify-function>
- <add-function signature="setAlignment(QFlags<Qt::AlignmentFlag>)">
+ <add-function signature="setAlignment(QFlags<Qt::AlignmentFlag>@alignment@)">
<inject-code class="target" position="beginning" file="../glue/qtwidgets.cpp" snippet="qlayout-setalignment"/>
</add-function>
<modify-function signature="removeItemWidget(QListWidgetItem*)" allow-thread="yes"/>
</object-type>
- <object-type name="QWidget" delete-in-main-thread="true">
+ <object-type name="QWidget" delete-in-main-thread="true" polymorphic-base="true">
<!-- see QWindow::nativeEvent(), QAbstractNativeEventFilter::nativeEventFilter() -->
<inject-code class="native" position="beginning" file="../glue/qtwidgets.cpp" snippet="qwidget-addaction-glue"/>
<inject-code class="native" position="beginning">
</modify-function>
<modify-function signature="parentWidget()const">
- <modify-argument index="this">
- <parent index="return" action="add"/>
- </modify-argument>
<modify-argument index="return">
<define-ownership class="target" owner="default"/>
</modify-argument>
</modify-function>
<extra-includes>
<include file-name="QPixmap" location="global"/>
+ <include file-name="qobjectconnect.h" location="global"/>
</extra-includes>
-
+ <add-function signature="open(PyCallable*@functor@)">
+ <inject-code class="target" position="beginning" file="../glue/qtwidgets.cpp"
+ snippet="qmessagebox-open-connect-accept"/>
+ </add-function>
<!-- FIXME PYSIDE-7: Remove deprecated overloads -->
<modify-function signature="critical(QWidget*,const QString&,const QString&,QFlags<QMessageBox::StandardButton>,QMessageBox::StandardButton)" allow-thread="yes"/>
<modify-function signature="critical(QWidget*,const QString&,const QString&,QMessageBox::StandardButton,QMessageBox::StandardButton)"
<modify-function signature="pixmap(Qt::ReturnByValueConstant)const" remove="all"/>
<modify-function signature="picture(Qt::ReturnByValueConstant)const" remove="all"/>
+ <!-- // FIXME PYSIDE7: PYSIDE-2808 Remove (resource cleanup)? -->
<modify-function signature="setMovie(QMovie *)">
<modify-argument index="1">
<parent index="this" action="add"/>
</modify-function>
<modify-function signature="del()" rename="del_"/>
- <add-function signature="addAction(QAction*)">
+ <add-function signature="addAction(QAction*@action@)">
<inject-code class="target" position="beginning" file="../glue/qtwidgets.cpp" snippet="qlineedit-addaction"/>
</add-function>
<include file-name="pysidecleanup.h" location="global"/>
</extra-includes>
<modify-function signature="QApplication(int&,char**,int)" access="private"/>
- <add-function signature="QApplication(QStringList)">
+ <add-function signature="QApplication(QStringList@arguments@)">
<inject-code file="../glue/qtwidgets.cpp" snippet="qapplication-1"/>
</add-function>
<add-function signature="QApplication()">
</value-type>
<modify-function signature="getLayoutPosition(QLayout*,int*,QFormLayout::ItemRole*)const">
- <modify-argument index="0">
- <replace-type modified-type="PyObject"/>
- </modify-argument>
<modify-argument index="2">
<remove-argument/>
</modify-argument>
<modify-argument index="3">
<remove-argument/>
</modify-argument>
+ <modify-argument index="return" pyi-type="Tuple[int, PySide6.QtWidgets.QFormLayout.ItemRole]">
+ <replace-type modified-type="PyTuple"/>
+ </modify-argument>
<inject-code class="target" position="beginning" file="../glue/qtwidgets.cpp" snippet="qformlayout-fix-args" />
</modify-function>
<modify-function signature="getWidgetPosition(QWidget*,int*,QFormLayout::ItemRole*)const">
</value-type>
</typesystem>
-
import os
import sys
from pathlib import Path
-from textwrap import dedent
+from types import ModuleType
+# mypy: disable-error-code="name-defined"
-# __all__ is also corrected below.
-__all__ = list("Qt" + body for body in
- "@all_module_shortnames@"
- .split(";"))
+# __all__ is computed below.
+__pre_all__ = [@init_modules@]
__version__ = "@FINAL_PACKAGE_VERSION@"
__version_info__ = (@BINDING_API_MAJOR_VERSION@, @BINDING_API_MINOR_VERSION@, @BINDING_API_MICRO_VERSION@, "@BINDING_API_PRE_RELEASE_VERSION_TYPE@", "@BINDING_API_PRE_RELEASE_VERSION@")
+SKIP_MYPY_TEST = bool("@SKIP_MYPY_TEST@")
+
def _additional_dll_directories(package_dir):
# Find shiboken6 relative to the package directory.
if root.suffix == '.zip':
return []
shiboken6 = root / 'shiboken6'
- if shiboken6.is_dir(): # Standard case, only shiboken6 is needed
+ if shiboken6.is_dir(): # Standard case, only shiboken6 is needed
return [shiboken6]
# The below code is for the build process when generate_pyi.py
# is executed in the build directory. We need libpyside and Qt in addition.
result.append(libpysideqml)
for path in os.environ.get('PATH').split(';'):
if path:
- if (Path(path) / 'qmake.exe').exists():
- result.append(path)
- break
+ if (Path(path) / 'qmake.exe').exists():
+ result.append(path)
+ break
return result
pyside_package_dir = Path(__file__).parent.resolve()
- if sys.platform == 'win32' and sys.version_info[0] == 3 and sys.version_info[1] >= 8:
+ if sys.platform == 'win32':
for dir in _additional_dll_directories(pyside_package_dir):
os.add_dll_directory(os.fspath(dir))
try:
# PYSIDE-1497: we use the build dir or install dir or site-packages, whatever the path
# setting dictates. There is no longer a difference in path structure.
+ global Shiboken
from shiboken6 import Shiboken
except Exception:
paths = ', '.join(sys.path)
# Since the wheel split, the __all__ variable cannot be computed statically,
# because we don't know all modules in advance.
- # Instead, we look into the file system and quickly build a list of all
- # existing .pyi files, because importing is not desired and also impossible during import.
- # By using the initially created list, we can keep some order intact.
+ # Instead, we use __getattr__ which is supported since Python 3.7
+ # and create the __all__ list on demand when needed.
location = Path(__file__).resolve().parent
-
- # Note: We should _not_ call this function while still building, but use the existing value!
- in_build = Path("@CMAKE_BINARY_DIR@") in location.parents
-
- if in_build:
- return __all__
-
files = os.listdir(location)
- unordered = set(name[:-4] for name in files if name.startswith("Qt") and name.endswith(".pyi"))
- ordered_part = __all__
+ unordered = set(name[: name.find(".")] for name in files if name.startswith("Qt") and (
+ name.endswith((".pyd", ".so"))))
+ ordered_part = __pre_all__
result = []
for name in ordered_part:
if name in unordered:
return result
-__all__ = _find_all_qt_modules()
+# Provide the __all__ variable only on access.
+def __getattr__(name: str) -> list[str]:
+ if name == "__all__":
+ global __all__
+ __all__ = _find_all_qt_modules()
+ return __all__
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}' :)")
+
+
+# Be prepared that people can access the module dict instead.
+class ModuleDict(dict):
+ def __missing__(self, key):
+ if key == "__all__":
+ self[key] = __all__ if "__all__" in globals() else __getattr__("__all__")
+ return __all__
+ raise KeyError(f"dict of module '{__name__}' has no key '{key}' :)")
+
+
+class SubModule(ModuleType):
+ pass
+
+
_setupQtDirectories()
+Shiboken.replaceModuleDict(sys.modules["PySide6"], SubModule, ModuleDict(globals()))
--- /dev/null
+// @snippet q_arg
+This function takes a type (or a type string) and a value of that type
+and returns an internal object that can be passed to
+:meth:`QMetaObject.invokeMethod`. See also Q_RETURN_ARG().
+// @snippet q_arg
+
+// @snippet q_return_arg
+This macro takes a type (or a type string) a value of which is then
+returned by :meth:`QMetaObject.invokeMethod`. See also Q_ARG().
+// @snippet q_return_arg
+
+// @snippet qlocale-system
+Returns a QLocale object initialized to the system locale.
+
+The system locale may use system-specific sources for locale data, where
+available, otherwise falling back on QLocale's built-in database entry for the
+language, script and territory the system reports.
+
+For example, on Windows, this locale will use the decimal/grouping characters and
+date/time formats specified in the system configuration panel.
+
+.. note:: Qt for Python on macOS will not reflect the user's region and language
+ preferences though QLocale::system(), but will instead reflect the
+ environment variables POSIX uses to specify locale, similar to Python's
+ locale module. If the system locale cannot be determined, which can be
+ due to none of the variables 'LC_ALL', 'LC_CTYPE', 'LANG' or 'LANGUAGE'
+ being set by your environment, then the default POSIX locale or
+ 'C' locale is returned.
+
+See also c().
+// @snippet qlocale-system
+
+// @snippet qabstractitemmodel-createindex
+Creates a model index for the given row and column with the internal pointer
+ptr. When using a :class:`QSortFilterProxyModel`, its indexes have their own
+internal pointer. It is not advisable to access this internal pointer outside
+of the model. Use the ``data()`` function instead.
+
+This function provides a consistent interface that model subclasses must use to
+create model indexes.
+
+.. warning:: Because of some Qt/Python integration rules, the ``ptr`` argument does
+ not get the reference incremented during the QModelIndex life time.
+ So it is necessary to keep the object used on ``ptr`` argument alive
+ during the whole process. Do not destroy the object if you are not
+ sure about that.
+// @snippet qabstractitemmodel-createindex
+
+// @snippet qobject-findChild
+To find the child of a certain :class:`QObject`, the first argument of this
+function should be the child's type, and the second the name of the child:
+
+::
+
+ ...
+ parent = QWidget()
+ ...
+ # The first argument must be the child type
+ child1 = parent.findChild(QPushButton, "child_button")
+ child2 = parent.findChild(QWidget, "child_widget")
+
+// @snippet qobject-findChild
+
+// @snippet qcoreapplication-init
+Constructs a Qt kernel application. Kernel applications are applications
+without a graphical user interface. These type of applications are used
+at the console or as server processes.
+
+The *args* argument is processed by the application, and made available
+in a more convenient form by the :meth:`~PySide6.QtCore.QCoreApplication.arguments()`
+method.
+// @snippet qcoreapplication-init
+
+// @snippet qsettings-value
+Custom overload that adds an optional named parameter to the function ``value()``
+to automatically cast the type that is being returned by the function.
+
+An example of this situation could be an ini file that contains
+the value of a one-element list::
+
+ settings.setValue('var', ['a'])
+
+The the ini file will be::
+
+ [General]
+ var=a # we cannot know that this is a list!
+
+Once we read it, we could specify if we want
+the default behavior, a str, or to cast the output
+to a list.
+
+ settings.value('var') # Will get "a"
+ settings.value('var', type=list) # Will get ["a"]
+
+// @snippet qsettings-value
--- /dev/null
+// @snippet qmlregistersingletoninstance
+.. py:function:: qmlRegisterSingletonInstance(pytype: type,\
+ uri: str,\
+ versionMajor: int,\
+ versionMinor: int,\
+ typeName: str,\
+ instanceObject: object) -> int
+
+ :param type pytype: Python class
+ :param str uri: uri to use while importing the component in QML
+ :param int versionMajor: major version
+ :param int versionMinor: minor version
+ :param str typeName: name exposed to QML
+ :param object instanceObject: singleton object to be registered
+ :return: int (the QML type id)
+
+This function registers a singleton Python object *instanceObject*, with a
+particular *uri* and *typeName*. Its version is a combination of *versionMajor*
+and *versionMinor*. Use this function to register an object of the given type
+*pytype* as a singleton type.
+// @snippet qmlregistersingletoninstance
+
+// @snippet qmlregistersingletontype_qobject_nocallback
+.. py:function:: qmlRegisterSingletonType(pytype: type, uri: str, versionMajor: int, versionMinor: int, typeName: str) -> int
+
+ :param type pytype: Python class
+ :param str uri: uri to use while importing the component in QML
+ :param int versionMajor: major version
+ :param int versionMinor: minor version
+ :param str typeName: name exposed to QML
+ :return: int (the QML type id)
+
+This function registers a Python type as a singleton in the QML system.
+
+Alternatively, the :ref:`QmlSingleton` decorator can be used.
+// @snippet qmlregistersingletontype_qobject_nocallback
+
+// @snippet qmlregistersingletontype_qobject_callback
+.. py:function:: qmlRegisterSingletonType(pytype: type, uri: str, versionMajor: int, versionMinor: int, typeName: str, callback: object) -> int
+
+ :param type pytype: Python class
+ :param str uri: uri to use while importing the component in QML
+ :param int versionMajor: major version
+ :param int versionMinor: minor version
+ :param str typeName: name exposed to QML
+ :param object callback: Python callable (to handle Python type)
+ :return: int (the QML type id)
+
+This function registers a Python type as a singleton in the QML system using
+the provided callback (which gets a QQmlEngine as a parameter) to generate the
+singleton.
+// @snippet qmlregistersingletontype_qobject_callback
+
+// @snippet qmlregistersingletontype_qjsvalue
+.. py:function:: qmlRegisterSingletonType(uri: str, versionMajor: int, versionMinor: int, typeName: str, callback: object) -> int
+
+ :param str uri: uri to use while importing the component in QML
+ :param int versionMajor: major version
+ :param int versionMinor: minor version
+ :param str typeName: name exposed to QML
+ :param object callback: Python callable (to handle QJSValue)
+ :return: int (the QML type id)
+
+This function registers a QJSValue as a singleton in the QML system using the
+provided callback (which gets a QQmlEngine as a parameter) to generate the
+singleton.
+// @snippet qmlregistersingletontype_qjsvalue
+
+// @snippet qmlregistertype
+.. py:function:: qmlRegisterType(pytype: type, uri: str, versionMajor: int, versionMinor: int, qmlName: str) -> int
+
+ :param type pytype: Python class
+ :param str uri: uri to use while importing the component in QML
+ :param int versionMajor: major version
+ :param int versionMinor: minor version
+ :param str qmlName: name exposed to QML
+ :return: int (the QML type id)
+
+This function registers the Python *type* in the QML system with the name
+*qmlName*, in the library imported from *uri* having the version number
+composed from *versionMajor* and *versionMinor*. For example, this registers a
+Python class 'MySliderItem' as a QML type named 'Slider' for version '1.0' of a
+module called 'com.mycompany.qmlcomponents':
+
+ ::
+
+ qmlRegisterType(MySliderItem, "com.mycompany.qmlcomponents", 1, 0, "Slider")
+
+Once this is registered, the type can be used in QML by importing the specified
+module name and version number:
+
+ ::
+
+ import com.mycompany.qmlcomponents 1.0
+
+ Slider { ... }
+
+Note that it's perfectly reasonable for a library to register types to older
+versions than the actual version of the library. Indeed, it is normal for the
+new library to allow QML written to previous versions to continue to work, even
+if more advanced versions of some of its types are available.
+// @snippet qmlregistertype
+
+// @snippet qmlregisteruncreatabletype
+.. py:function:: qmlRegisterUncreatableType(pytype: type, uri: str, versionMajor: int, versionMinor: int, qmlName: str, noCreationReason: str) -> int
+
+ :param type pytype: Python class
+ :param str uri: uri to use while importing the component in QML
+ :param int versionMajor: major version
+ :param int versionMinor: minor version
+ :param str qmlName: name exposed to QML
+ :param str noCreationReason: Error message shown when trying to create the QML type
+ :return: int (the QML type id)
+
+This function registers the Python *type* in the QML system as an uncreatable
+type with the name *qmlName*, in the library imported from *uri* having the
+version number composed from *versionMajor* and *versionMinor*, showing
+*noCreationReason* as an error message when creating the type is attempted. For
+example, this registers a Python class 'MySliderItem' as a QML type named
+'Slider' for version '1.0' of a module called 'com.mycompany.qmlcomponents':
+
+ ::
+ qmlRegisterUncreatableType(MySliderItem, "com.mycompany.qmlcomponents", 1, 0, "Slider", "Slider cannot be created.")
+
+Note that it's perfectly reasonable for a library to register types to older
+versions than the actual version of the library. Indeed, it is normal for the
+new library to allow QML written to previous versions to continue to work, even
+if more advanced versions of some of its types are available.
+
+Alternatively, the :ref:`QmlUncreatable` decorator can be used.
+// @snippet qmlregisteruncreatabletype
+
+// @snippet qqmlengine-singletoninstance-qmltypeid
+Returns the instance of a singleton type that was registered under qmlTypeId.
+For ``QObject``-derived singleton types, the ``QObject`` instance is returned,
+otherwise a ``QJSValue`` or ``None``.
+
+It is recommended to store the QML type id, e.g. as a static member in the
+singleton class. The lookup via qmlTypeId() is costly.
+// @snippet qqmlengine-singletoninstance-qmltypeid
+
+// @snippet qqmlengine-singletoninstance-typename Returns the instance of a
+singleton type named typeName from the module specified by uri.
+For ``QObject``-derived singleton types, the ``QObject`` instance is returned,
+otherwise a ``QJSValue`` or ``None``.
+
+This method can be used as an alternative to calling qmlTypeId followed by the
+id based overload of singletonInstance. This is convenient when one only needs
+to do a one time setup of a singleton; if repeated access to the singleton is
+required, caching its typeId will allow faster subsequent access via the
+type-id based overload.
+// @snippet qqmlengine-singletoninstance-typename
+++ /dev/null
-// @snippet qmlregistersingletoninstance
-.. py:function:: qmlRegisterSingletonInstance(pytype: type,\
- uri: str,\
- versionMajor: int,\
- versionMinor: int,\
- typeName: str,\
- instanceObject: object) -> int
-
- :param type pytype: Python class
- :param str uri: uri to use while importing the component in QML
- :param int versionMajor: major version
- :param int versionMinor: minor version
- :param str typeName: name exposed to QML
- :param object instanceObject: singleton object to be registered
- :return: int (the QML type id)
-
-This function registers a singleton Python object *instanceObject*, with a
-particular *uri* and *typeName*. Its version is a combination of *versionMajor*
-and *versionMinor*. Use this function to register an object of the given type
-*pytype* as a singleton type.
-// @snippet qmlregistersingletoninstance
-
-// @snippet qmlregistersingletontype_qobject_nocallback
-.. py:function:: qmlRegisterSingletonType(pytype: type, uri: str, versionMajor: int, versionMinor: int, typeName: str) -> int
-
- :param type pytype: Python class
- :param str uri: uri to use while importing the component in QML
- :param int versionMajor: major version
- :param int versionMinor: minor version
- :param str typeName: name exposed to QML
- :return: int (the QML type id)
-
-This function registers a Python type as a singleton in the QML system.
-
-Alternatively, the :ref:`QmlSingleton` decorator can be used.
-// @snippet qmlregistersingletontype_qobject_nocallback
-
-// @snippet qmlregistersingletontype_qobject_callback
-.. py:function:: qmlRegisterSingletonType(pytype: type, uri: str, versionMajor: int, versionMinor: int, typeName: str, callback: object) -> int
-
- :param type pytype: Python class
- :param str uri: uri to use while importing the component in QML
- :param int versionMajor: major version
- :param int versionMinor: minor version
- :param str typeName: name exposed to QML
- :param object callback: Python callable (to handle Python type)
- :return: int (the QML type id)
-
-This function registers a Python type as a singleton in the QML system using
-the provided callback (which gets a QQmlEngine as a parameter) to generate the
-singleton.
-// @snippet qmlregistersingletontype_qobject_callback
-
-// @snippet qmlregistersingletontype_qjsvalue
-.. py:function:: qmlRegisterSingletonType(uri: str, versionMajor: int, versionMinor: int, typeName: str, callback: object) -> int
-
- :param str uri: uri to use while importing the component in QML
- :param int versionMajor: major version
- :param int versionMinor: minor version
- :param str typeName: name exposed to QML
- :param object callback: Python callable (to handle QJSValue)
- :return: int (the QML type id)
-
-This function registers a QJSValue as a singleton in the QML system using the
-provided callback (which gets a QQmlEngine as a parameter) to generate the
-singleton.
-// @snippet qmlregistersingletontype_qjsvalue
-
-// @snippet qmlregistertype
-.. py:function:: qmlRegisterType(pytype: type, uri: str, versionMajor: int, versionMinor: int, qmlName: str) -> int
-
- :param type pytype: Python class
- :param str uri: uri to use while importing the component in QML
- :param int versionMajor: major version
- :param int versionMinor: minor version
- :param str qmlName: name exposed to QML
- :return: int (the QML type id)
-
-This function registers the Python *type* in the QML system with the name
-*qmlName*, in the library imported from *uri* having the version number
-composed from *versionMajor* and *versionMinor*. For example, this registers a
-Python class 'MySliderItem' as a QML type named 'Slider' for version '1.0' of a
-module called 'com.mycompany.qmlcomponents':
-
- ::
-
- qmlRegisterType(MySliderItem, "com.mycompany.qmlcomponents", 1, 0, "Slider")
-
-Once this is registered, the type can be used in QML by importing the specified
-module name and version number:
-
- ::
-
- import com.mycompany.qmlcomponents 1.0
-
- Slider { ... }
-
-Note that it's perfectly reasonable for a library to register types to older
-versions than the actual version of the library. Indeed, it is normal for the
-new library to allow QML written to previous versions to continue to work, even
-if more advanced versions of some of its types are available.
-// @snippet qmlregistertype
-
-// @snippet qmlregisteruncreatabletype
-.. py:function:: qmlRegisterUncreatableType(pytype: type, uri: str, versionMajor: int, versionMinor: int, qmlName: str, noCreationReason: str) -> int
-
- :param type pytype: Python class
- :param str uri: uri to use while importing the component in QML
- :param int versionMajor: major version
- :param int versionMinor: minor version
- :param str qmlName: name exposed to QML
- :param str noCreationReason: Error message shown when trying to create the QML type
- :return: int (the QML type id)
-
-This function registers the Python *type* in the QML system as an uncreatable
-type with the name *qmlName*, in the library imported from *uri* having the
-version number composed from *versionMajor* and *versionMinor*, showing
-*noCreationReason* as an error message when creating the type is attempted. For
-example, this registers a Python class 'MySliderItem' as a QML type named
-'Slider' for version '1.0' of a module called 'com.mycompany.qmlcomponents':
-
- ::
- qmlRegisterUncreatableType(MySliderItem, "com.mycompany.qmlcomponents", 1, 0, "Slider", "Slider cannot be created.")
-
-Note that it's perfectly reasonable for a library to register types to older
-versions than the actual version of the library. Indeed, it is normal for the
-new library to allow QML written to previous versions to continue to work, even
-if more advanced versions of some of its types are available.
-
-Alternatively, the :ref:`QmlUncreatable` decorator can be used.
-// @snippet qmlregisteruncreatabletype
-
-// @snippet qqmlengine-singletoninstance-qmltypeid
-Returns the instance of a singleton type that was registered under qmlTypeId.
-For ``QObject``-derived singleton types, the ``QObject`` instance is returned,
-otherwise a ``QJSValue`` or ``None``.
-
-It is recommended to store the QML type id, e.g. as a static member in the
-singleton class. The lookup via qmlTypeId() is costly.
-// @snippet qqmlengine-singletoninstance-qmltypeid
-
-// @snippet qqmlengine-singletoninstance-typename Returns the instance of a
-singleton type named typeName from the module specified by uri.
-For ``QObject``-derived singleton types, the ``QObject`` instance is returned,
-otherwise a ``QJSValue`` or ``None``.
-
-This method can be used as an alternative to calling qmlTypeId followed by the
-id based overload of singletonInstance. This is convenient when one only needs
-to do a one time setup of a singleton; if repeated access to the singleton is
-required, caching its typeId will allow faster subsequent access via the
-type-id based overload.
-// @snippet qqmlengine-singletoninstance-typename
--- /dev/null
+// @snippet qwebenginepage-async-note
+.. note:: We guarantee that the ``resultCallback`` is always called, but it
+ might be done during page destruction. When ``QWebEnginePage``
+ is deleted, the callback is triggered with an invalid value and it
+ is not safe to use the corresponding ``QWebEnginePage``,
+ ``QWebEngineFrame``, or ``QWebEngineView`` instance inside it.
+// @snippet qwebenginepage-async-note
+
+// @snippet qwebenginepage-findtext
+Finds the specified string, ``subString``, in the page, using the given
+``options``. The ``findTextFinished()`` signal is emitted when a string search
+is completed.
+
+To clear the search highlight, just pass an empty string.
+
+The ``resultCallback`` must take a ``QWebEngineFindTextResult`` parameter.
+// @snippet qwebenginepage-findtext
+
+// @snippet qwebenginepage-tohtml
+Asynchronous method to retrieve the page's content as HTML, enclosed in HTML
+and BODY tags. Upon successful completion, ``resultCallback`` is called with
+the page's content.
+// @snippet qwebenginepage-tohtml
+
+// @snippet qwebenginepage-toplaintext
+Asynchronous method to retrieve the page's content converted to plain text,
+completely stripped of all HTML formatting.
+
+Upon successful completion, ``resultCallback`` is called with the page's content.
+// @snippet qwebenginepage-toplaintext
+
+// @snippet qwebenginepage-runjavascript
+Runs the JavaScript code contained in ``scriptSource`` script on this frame,
+without checking whether the DOM of the page has been constructed.
+
+To avoid conflicts with other scripts executed on the page, the world in which
+the script is run is specified by ``worldId``. The world ID values are the same
+as provided by ``QWebEngineScript.ScriptWorldId``, and between 0 and 256. If
+you leave out the world ID, the script is run in the ``MainWorld`` (0).
+
+When the script has been executed, the callable ``resultCallback`` is called
+with the result of the last executed statement.
+
+Only plain data can be returned from JavaScript as the result value.
+
+.. note:: Do not execute lengthy routines in the callback function, because
+ it might block the rendering of the web engine page.
+// @snippet qwebenginepage-runjavascript
+
+// @snippet qwebenginepage-printtopdf
+Renders the current content of the page into a PDF document and returns a byte
+array containing the PDF data as parameter to ``resultCallback``.
+
+The page size and orientation of the produced PDF document are taken from the
+values specified in ``layout``, while the range of pages printed is taken from
+``ranges`` with the default being printing all pages.
+
+.. note:: The ``QWebEnginePage.WebAction.Stop`` web action can be used to
+ interrupt this operation.
+// @snippet qwebenginepage-printtopdf
+
+// @snippet qwebenginepage-findframebyname
+Returns the frame with the given ``name``. If there are multiple frames with
+the same name, which one is returned is arbitrary. If no frame was found,
+returns ``None``.
+// @snippet qwebenginepage-findframebyname
+
+// @snippet qwebengineframe-printtopdf
+Renders the current content of the frame into a PDF document and returns a byte
+array containing the PDF data as parameter to ``resultCallback``. Printing uses
+a page size of A4, portrait layout, and includes the full range of pages.
+
+.. note:: The ``QWebEnginePage.WebAction.Stop`` web action can be used to
+ interrupt this operation.
+// @snippet qwebengineframe-printtopdf
Shiboken::GilState state;
auto *requestPtr = &request;
Shiboken::AutoDecRef arglist(PyTuple_New(1));
- PyTuple_SET_ITEM(arglist, 0,
+ PyTuple_SetItem(arglist, 0,
%CONVERTTOPYTHON[QHttpServerRequest *](requestPtr));
PyObject *ret = PyObject_CallObject(callable, arglist);
if (PyErr_Occurred())
%PYARG_0 = %CONVERTTOPYTHON[bool](cppResult);
// @snippet qhttpserver-route
-// @snippet qhttpserver-afterrequest
-auto *callable = %PYARG_1;
+// @snippet qhttpserver-addafterrequesthandler
+auto *callable = %PYARG_2;
-%CPPSELF.%FUNCTION_NAME([callable](QHttpServerResponse &&response,
- const QHttpServerRequest &request) {
+auto callback = [callable](const QHttpServerRequest &request,
+ QHttpServerResponse &response) {
Shiboken::GilState state;
Shiboken::AutoDecRef arglist(PyTuple_New(2));
auto *responsePtr = &response;
auto *requestPtr = &request;
- PyTuple_SET_ITEM(arglist, 0,
- %CONVERTTOPYTHON[QHttpServerResponse *](responsePtr));
- PyTuple_SET_ITEM(arglist, 1,
+ PyTuple_SetItem(arglist, 0,
%CONVERTTOPYTHON[QHttpServerRequest *](requestPtr));
+ PyTuple_SetItem(arglist, 1,
+ %CONVERTTOPYTHON[QHttpServerResponse *](responsePtr));
PyObject_CallObject(callable, arglist);
if (PyErr_Occurred())
PyErr_Print();
- return std::move(response);
-});
-// @snippet qhttpserver-afterrequest
+};
+
+%CPPSELF.%FUNCTION_NAME(%1, callback);
+// @snippet qhttpserver-addafterrequesthandler
Shiboken::AutoDecRef keys(PyDict_Keys(map));
if (!QVariant_isStringList(keys))
return {};
- PyObject *key;
- PyObject *value;
+ PyObject *key{};
+ PyObject *value{};
QMap<QString,QVariant> ret;
while (PyDict_Next(map, &pos, &key, &value)) {
QString cppKey = %CONVERTTOCPP[QString](key);
QList<QVariant> lst;
Shiboken::AutoDecRef fast(PySequence_Fast(list, "Failed to convert QVariantList"));
- const Py_ssize_t size = PySequence_Fast_GET_SIZE(fast.object());
+ const Py_ssize_t size = PySequence_Size(fast.object());
for (Py_ssize_t i = 0; i < size; ++i) {
- PyObject *pyItem = PySequence_Fast_GET_ITEM(fast.object(), i);
+ Shiboken::AutoDecRef pyItem(PySequence_GetItem(fast.object(), i));
QVariant item = %CONVERTTOCPP[QVariant](pyItem);
lst.append(item);
}
QList<QByteArray> version = QByteArray(qVersion()).split('.');
PyObject *pyQtVersion = PyTuple_New(3);
for (int i = 0; i < 3; ++i)
- PyTuple_SET_ITEM(pyQtVersion, i, PyLong_FromLong(version[i].toInt()));
+ PyTuple_SetItem(pyQtVersion, i, PyLong_FromLong(version[i].toInt()));
PyModule_AddObject(module, "__version_info__", pyQtVersion);
PyModule_AddStringConstant(module, "__version__", qVersion());
// @snippet qt-version
// @snippet qobject-connect-6
// @snippet qobject-emit
-%RETURN_TYPE %0 = PySide::SignalManager::instance().emitSignal(%CPPSELF, %1, %PYARG_2);
+%RETURN_TYPE %0 = PySide::SignalManager::emitSignal(%CPPSELF, %1, %PYARG_2);
%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
// @snippet qobject-emit
PySide::registerInternalQtConf();
PySide::init(module);
-Py_AtExit(QtCoreModuleExit);
// @snippet qt-pysideinit
// @snippet qt-messagehandler
{
Shiboken::GilState state;
Shiboken::AutoDecRef arglist(PyTuple_New(3));
- PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QtMsgType](type));
- PyTuple_SET_ITEM(arglist, 1, %CONVERTTOPYTHON[QMessageLogContext &](ctx));
+ PyTuple_SetItem(arglist, 0, %CONVERTTOPYTHON[QtMsgType](type));
+ PyTuple_SetItem(arglist, 1, %CONVERTTOPYTHON[QMessageLogContext &](ctx));
QByteArray array = msg.toUtf8(); // Python handler requires UTF-8
const char *data = array.constData();
- PyTuple_SET_ITEM(arglist, 2, %CONVERTTOPYTHON[const char *](data));
+ PyTuple_SetItem(arglist, 2, %CONVERTTOPYTHON[const char *](data));
Shiboken::AutoDecRef ret(PyObject_CallObject(qtmsghandler, arglist));
}
-static void QtCoreModuleExit()
-{
- PySide::SignalManager::instance().clear();
-}
// @snippet qt-messagehandler
// @snippet qt-installmessagehandler
QPointF p;
%RETURN_TYPE retval = %CPPSELF.%FUNCTION_NAME(%ARGUMENT_NAMES, &p);
%PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QPointF](p));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QPointF](p));
// @snippet qlinef-intersect
// @snippet qresource-data
int year, month, day;
%CPPSELF.%FUNCTION_NAME(&year, &month, &day);
%PYARG_0 = PyTuple_New(3);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[int](year));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[int](month));
-PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[int](day));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[int](year));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[int](month));
+PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[int](day));
// @snippet qdate-getdate
// @snippet qdate-weeknumber
int yearNumber;
int week = %CPPSELF.%FUNCTION_NAME(&yearNumber);
%PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[int](week));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[int](yearNumber));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[int](week));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[int](yearNumber));
// @snippet qdate-weeknumber
// @snippet qdatetime-1
QDate date(%1, %2, %3);
QTime time(%4, %5, %6, %7);
-%0 = new %TYPE(date, time, Qt::TimeSpec(%8));
+%0 = new %TYPE(date, time,
+ Qt::TimeSpec(%8) == Qt::UTC
+ ? QTimeZone(QTimeZone::UTC) : QTimeZone(QTimeZone::LocalTime));
+Shiboken::Warnings::warnDeprecated("QDateTime", "QDateTime(..., Qt::TimeSpec spec)");
// @snippet qdatetime-1
// @snippet qdatetime-2
// @snippet qdatetime-3
QDate date(%1, %2, %3);
QTime time(%4, %5, %6, %7);
-%0 = new %TYPE(date, time, %8);
+%0 = new %TYPE(date, time,
+ %8 == Qt::UTC ? QTimeZone(QTimeZone::UTC) : QTimeZone(QTimeZone::LocalTime));
+Shiboken::Warnings::warnDeprecated("QDateTime", "QDateTime(..., Qt::TimeSpec spec)");
// @snippet qdatetime-3
// @snippet qdatetime-topython
// @snippet qbitarray-len
// @snippet qbitarray-getitem
-if (_i < 0 || _i >= %CPPSELF.size()) {
- PyErr_SetString(PyExc_IndexError, "index out of bounds");
- return 0;
+const Py_ssize_t size = %CPPSELF.size();
+if (_i < 0 || _i >= size) {
+ Shiboken::Errors::setIndexOutOfBounds(_i, 0, size);
+ return nullptr;
}
bool ret = %CPPSELF.at(_i);
return %CONVERTTOPYTHON[bool](ret);
return !result ? -1 : 0;
// @snippet qbitarray-setitem
+// @snippet qmodelroledata-setdata
+// Call template <typename T> void QModelRoleData::setData(T &&value)
+%CPPSELF.%FUNCTION_NAME(%1);
+// @snippet qmodelroledata-setdata
+
+// @snippet qmodelroledataspan-len
+return %CPPSELF.size();
+// @snippet qmodelroledataspan-len
+
+// @snippet qmodelroledataspan-getitem
+const Py_ssize_t size = %CPPSELF.size();
+if (_i < 0 || _i >= size) {
+ Shiboken::Errors::setIndexOutOfBounds(_i, 0, size);
+ return nullptr;
+}
+// Return a pointer to allow for modification using QModelRoleData::setData()
+QModelRoleData *item = &((*%CPPSELF)[_i]);
+return %CONVERTTOPYTHON[QModelRoleData *](item);
+// @snippet qmodelroledataspan-getitem
+
// @snippet default-enter
Py_INCREF(%PYSELF);
pyResult = %PYSELF;
// @snippet qbytearray-mgetitem
if (PyIndex_Check(_key)) {
const Py_ssize_t _i = PyNumber_AsSsize_t(_key, PyExc_IndexError);
- if (_i < 0 || _i >= %CPPSELF.size())
- return PyErr_Format(PyExc_IndexError, "index out of bounds");
+ const Py_ssize_t size = %CPPSELF.size();
+ if (_i < 0 || _i >= size) {
+ Shiboken::Errors::setIndexOutOfBounds(_i, 0, size);
+ return nullptr;
+ }
char res[2] = {%CPPSELF.at(_i), '\0'};
return PyBytes_FromStringAndSize(res, 1);
}
// @snippet qbytearray-bufferprotocol
// @snippet qbytearray-operatorplus-1
-QByteArray ba = QByteArray(PyBytes_AS_STRING(%PYARG_1), PyBytes_GET_SIZE(%PYARG_1)) + *%CPPSELF;
+QByteArray ba = QByteArray(PyBytes_AsString(%PYARG_1), PyBytes_Size(%PYARG_1)) + *%CPPSELF;
%PYARG_0 = %CONVERTTOPYTHON[QByteArray](ba);
// @snippet qbytearray-operatorplus-1
// @snippet qbytearray-operatorequalequal
if (PyUnicode_CheckExact(%PYARG_1)) {
Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1));
- QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object()));
+ QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_Size(data.object()));
bool cppResult = %CPPSELF == ba;
%PYARG_0 = %CONVERTTOPYTHON[bool](cppResult);
}
// @snippet qbytearray-operatornotequal
if (PyUnicode_CheckExact(%PYARG_1)) {
Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1));
- QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object()));
+ QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_Size(data.object()));
bool cppResult = %CPPSELF != ba;
%PYARG_0 = %CONVERTTOPYTHON[bool](cppResult);
}
// @snippet qbytearray-operatorgreater
if (PyUnicode_CheckExact(%PYARG_1)) {
Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1));
- QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object()));
+ QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_Size(data.object()));
bool cppResult = %CPPSELF > ba;
%PYARG_0 = %CONVERTTOPYTHON[bool](cppResult);
}
// @snippet qbytearray-operatorgreaterequal
if (PyUnicode_CheckExact(%PYARG_1)) {
Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1));
- QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object()));
+ QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_Size(data.object()));
bool cppResult = %CPPSELF >= ba;
%PYARG_0 = %CONVERTTOPYTHON[bool](cppResult);
}
// @snippet qbytearray-operatorlower
if (PyUnicode_CheckExact(%PYARG_1)) {
Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1));
- QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object()));
+ QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_Size(data.object()));
bool cppResult = %CPPSELF < ba;
%PYARG_0 = %CONVERTTOPYTHON[bool](cppResult);
}
// @snippet qbytearray-operatorlowerequal
if (PyUnicode_CheckExact(%PYARG_1)) {
Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1));
- QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object()));
+ QByteArray ba = QByteArray(PyBytes_AsString(data.object()), PyBytes_Size(data.object()));
bool cppResult = %CPPSELF <= ba;
%PYARG_0 = %CONVERTTOPYTHON[bool](cppResult);
}
// @snippet qbytearray-2
// @snippet qbytearray-3
-%0 = new QByteArray(PyBytes_AS_STRING(%PYARG_1), PyBytes_GET_SIZE(%PYARG_1));
+%0 = new QByteArray(PyBytes_AsString(%PYARG_1), PyBytes_Size(%PYARG_1));
// @snippet qbytearray-3
// @snippet qbytearray-py3
// @snippet qbytearray-len
// @snippet qbytearray-getitem
-if (_i < 0 || _i >= %CPPSELF.size()) {
- PyErr_SetString(PyExc_IndexError, "index out of bounds");
- return 0;
-} else {
- char res[2];
- res[0] = %CPPSELF.at(_i);
- res[1] = 0;
- return PyBytes_FromStringAndSize(res, 1);
+const Py_ssize_t size = %CPPSELF.size();
+if (_i < 0 || _i >= size) {
+ Shiboken::Errors::setIndexOutOfBounds(_i, 0, size);
+ return nullptr;
}
+
+char res[2];
+res[0] = %CPPSELF.at(_i);
+res[1] = 0;
+return PyBytes_FromStringAndSize(res, 1);
// @snippet qbytearray-getitem
// @snippet qbytearray-setitem
QTimer * timer = %CONVERTTOCPP[QTimer *](pyTimer);
timer->setSingleShot(true);
-
- if (PyObject_TypeCheck(%2, PySideSignalInstance_TypeF())) {
- PySideSignalInstance *signalInstance = reinterpret_cast<PySideSignalInstance *>(%2);
- Shiboken::AutoDecRef signalSignature(Shiboken::String::fromFormat("2%s", PySide::Signal::getSignature(signalInstance)));
- Shiboken::AutoDecRef result(
- PyObject_CallMethod(pyTimer, "connect", "OsOO",
- pyTimer,
- SIGNAL(timeout()),
- PySide::Signal::getObject(signalInstance),
- signalSignature.object())
- );
- } else {
- Shiboken::AutoDecRef result(
- PyObject_CallMethod(pyTimer, "connect", "OsO",
- pyTimer,
- SIGNAL(timeout()),
- %PYARG_2)
- );
- }
+ if (!PySide::callConnect(pyTimer, SIGNAL(timeout()), %PYARG_2))
+ return nullptr;
timer->connect(timer, &QTimer::timeout, timer, &QObject::deleteLater, Qt::DirectConnection);
Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject *>(pyTimer));
qint64 pid;
%RETURN_TYPE retval = %TYPE::%FUNCTION_NAME(%1, %2, %3, &pid);
%PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[qint64](pid));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[qint64](pid));
// @snippet qprocess-startdetached
// @snippet qcoreapplication-init
{
static int argc;
static char **argv;
- PyObject *stringlist = PyTuple_GET_ITEM(pyargv, 0);
+ PyObject *stringlist = PyTuple_GetItem(pyargv, 0);
if (Shiboken::listToArgcArgv(stringlist, &argc, &argv, "PySideApp")) {
*cptr = new QCoreApplicationWrapper(argc, argv);
Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject *>(self));
// @snippet qeasingcurve
// @snippet qeasingcurve-setcustomtype
-QEasingCurve::EasingFunction func = PySideEasingCurveFunctor::createCustomFuntion(%PYSELF, %PYARG_1);
+QEasingCurve::EasingFunction func = PySideEasingCurveFunctor::createCustomFunction(%PYSELF, %PYARG_1);
if (func)
%CPPSELF.%FUNCTION_NAME(func);
// @snippet qeasingcurve-setcustomtype
// @snippet qt-registerresourcedata
// @snippet qt-qregisterresourcedata
-%RETURN_TYPE %0 = %FUNCTION_NAME(%1, reinterpret_cast<uchar *>(PyBytes_AS_STRING(%PYARG_2)),
- reinterpret_cast<uchar *>(PyBytes_AS_STRING(%PYARG_3)),
- reinterpret_cast<uchar *>(PyBytes_AS_STRING(%PYARG_4)));
+%RETURN_TYPE %0 = %FUNCTION_NAME(%1, reinterpret_cast<uchar *>(PyBytes_AsString(%PYARG_2)),
+ reinterpret_cast<uchar *>(PyBytes_AsString(%PYARG_3)),
+ reinterpret_cast<uchar *>(PyBytes_AsString(%PYARG_4)));
%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
// @snippet qt-qregisterresourcedata
// @snippet qt-qunregisterresourcedata
-%RETURN_TYPE %0 = %FUNCTION_NAME(%1, reinterpret_cast<uchar *>(PyBytes_AS_STRING(%PYARG_2)),
- reinterpret_cast<uchar *>(PyBytes_AS_STRING(%PYARG_3)),
- reinterpret_cast<uchar *>(PyBytes_AS_STRING(%PYARG_4)));
+%RETURN_TYPE %0 = %FUNCTION_NAME(%1, reinterpret_cast<uchar *>(PyBytes_AsString(%PYARG_2)),
+ reinterpret_cast<uchar *>(PyBytes_AsString(%PYARG_3)),
+ reinterpret_cast<uchar *>(PyBytes_AsString(%PYARG_4)));
%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
// @snippet qt-qunregisterresourcedata
-// @snippet use-stream-for-format-security
-// Uses the stream version for security reasons
-// see gcc man page at -Wformat-security
+// @snippet qdebug-format-string
Py_BEGIN_ALLOW_THREADS
-%FUNCTION_NAME() << %1;
+%FUNCTION_NAME("%s", %1); // Uses placeholder for security reasons
Py_END_ALLOW_THREADS
-// @snippet use-stream-for-format-security
+// @snippet qdebug-format-string
// @snippet qresource-registerResource
auto ptr = reinterpret_cast<uchar *>(Shiboken::Buffer::getPointer(%PYARG_1));
// @snippet return-readData
%RETURN_TYPE %0 = 0;
if (PyBytes_Check(%PYARG_0)) {
- %0 = PyBytes_GET_SIZE(%PYARG_0.object());
- memcpy(%1, PyBytes_AS_STRING(%PYARG_0.object()), %0);
+ %0 = PyBytes_Size(%PYARG_0.object());
+ memcpy(%1, PyBytes_AsString(%PYARG_0.object()), %0);
} else if (Shiboken::String::check(%PYARG_0.object())) {
%0 = Shiboken::String::len(%PYARG_0.object());
memcpy(%1, Shiboken::String::toCString(%PYARG_0.object()), %0);
if (!atexit.isNull() && !regFunc.isNull()){
PyObject *shutDownFunc = PyObject_GetAttrString(module, "__moduleShutdown");
Shiboken::AutoDecRef args(PyTuple_New(1));
- PyTuple_SET_ITEM(args, 0, shutDownFunc);
- Shiboken::AutoDecRef retval(PyObject_Call(regFunc, args, 0));
+ PyTuple_SetItem(args, 0, shutDownFunc);
+ Shiboken::AutoDecRef retval(PyObject_Call(regFunc, args, nullptr));
Q_ASSERT(!retval.isNull());
}
}
// @snippet conversion-qtime-pytime
// @snippet conversion-qbytearray-pybytes
-%out = %OUTTYPE(PyBytes_AS_STRING(%in), PyBytes_GET_SIZE(%in));
+%out = %OUTTYPE(PyBytes_AsString(%in), PyBytes_Size(%in));
// @snippet conversion-qbytearray-pybytes
// @snippet conversion-qbytearray-pybytearray
return converterO.value().toPython(cppInRef.data());
PyErr_Format(PyExc_RuntimeError, "Can't find converter for '%s'.", %in.typeName());
-return 0;
+return nullptr;
// @snippet return-qvariant
// @snippet return-qjsonobject
}
// @snippet qdatastream-read-bytes
-// @snippet qloggingcategory_to_cpp
-// PYSIDE-2404: Usage of the `get()` function not necessary, the type exists.
- QLoggingCategory *category{nullptr};
- Shiboken::Conversions::pythonToCppPointer(SbkPySide6_QtCoreTypeStructs[SBK_QLoggingCategory_IDX].type,
- pyArgs[0], &(category));
-// @snippet qloggingcategory_to_cpp
-
// Q_ARG()-equivalent
// @snippet q_arg
const QArgData qArgData = qArgDataFromPyType(%1);
bool arg_qpermission = (classMethod && (count == 2)) || (!classMethod && (count == 1));
-auto callback = [callable, count, arg_qpermission](const QPermission &permission) -> void
+auto callback = [callable, arg_qpermission](const QPermission &permission) -> void
{
Shiboken::GilState state;
if (arg_qpermission) {
Shiboken::AutoDecRef arglist(PyTuple_New(1));
- PyTuple_SET_ITEM(arglist.object(), 0, %CONVERTTOPYTHON[QPermission](permission));
+ PyTuple_SetItem(arglist.object(), 0, %CONVERTTOPYTHON[QPermission](permission));
Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
} else {
Shiboken::AutoDecRef ret(PyObject_CallObject(callable, nullptr));
QString hostname, appname;
%CPPSELF.%FUNCTION_NAME(&pid, &hostname, &appname);
%PYARG_0 = PyTuple_New(3);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[qint64](pid));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QString](hostname));
-PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[QString](appname));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[qint64](pid));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QString](hostname));
+PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[QString](appname));
// @snippet qlockfile-getlockinfo
// @snippet darwin_permission_plugin
)PY", Py_file_input, _inputDict, _inputDict);
// @snippet qt-modifier
+
+// @snippet qdirlisting-iter
+auto result = QtCoreHelper::QDirListingIterator(*%CPPSELF);
+%PYARG_0 = %CONVERTTOPYTHON[QtCoreHelper::QDirListingIterator](result);
+// @snippet qdirlisting-iter
+
+// @snippet qdirlistingiterator-next
+if (%CPPSELF.next()) {
+ Py_INCREF(%PYSELF);
+ %PYARG_0 = %PYSELF;
+}
+// @snippet qdirlistingiterator-next
+
+// @snippet qdirlisting-direntry-repr
+QByteArray result = '<' + QByteArray(Py_TYPE(%PYSELF)->tp_name)
+ + " object at 0x"
+ + QByteArray::number(quintptr(%PYSELF), 16) + " (\""
+ + %CPPSELF.absoluteFilePath().toUtf8() + "\")>";
+%PYARG_0 = Shiboken::String::fromCString(result.constData());
+// @snippet qdirlisting-direntry-repr
{
Shiboken::GilState state;
Shiboken::AutoDecRef arglist(PyTuple_New(2));
- PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QString](key));
- PyTuple_SET_ITEM(arglist, 1, %CONVERTTOPYTHON[QObject *](o));
+ PyTuple_SetItem(arglist, 0, %CONVERTTOPYTHON[QString](key));
+ PyTuple_SetItem(arglist, 1, %CONVERTTOPYTHON[QObject *](o));
for (auto *f : m_factoryFunctions) {
if (PyObject *pyResult = PyObject_CallObject(f, arglist)) {
// @snippet qtextline-cursortox
%RETURN_TYPE %0 = %CPPSELF->::%TYPE::%FUNCTION_NAME(&%1, %2);
%PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](%0));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](%0));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
// @snippet qtextline-cursortox
// @snippet qkeysequence-getitem
-if (_i < 0 || _i >= %CPPSELF.count()) {
- PyErr_SetString(PyExc_IndexError, "index out of bounds");
- return 0;
+const Py_ssize_t size = %CPPSELF.count();
+if (_i < 0 || _i >= size) {
+ Shiboken::Errors::setIndexOutOfBounds(_i, 0, size);
+ return nullptr;
}
QKeyCombination item = (*%CPPSELF)[_i];
return %CONVERTTOPYTHON[QKeyCombination](item);
// @snippet qimage-scanline
// @snippet qcolor-setstate
-Shiboken::AutoDecRef func(PyObject_GetAttr(%PYSELF, PyTuple_GET_ITEM(%1, 0)));
-PyObject *args = PyTuple_GET_ITEM(%1, 1);
+Shiboken::AutoDecRef func(PyObject_GetAttr(%PYSELF, PyTuple_GetItem(%1, 0)));
+PyObject *args = PyTuple_GetItem(%1, 1);
%PYARG_0 = PyObject_Call(func, args, nullptr);
// @snippet qcolor-setstate
%RETURN_TYPE retval_ = %CPPSELF.%FUNCTION_NAME(%1, %2);
%END_ALLOW_THREADS
%PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
// @snippet qclipboard-text
// @snippet qpainter-drawpointsnp-numpy-x-y
if (PySequence_Size(seq) == 16) {
float values[16];
for (Py_ssize_t i = 0; i < 16; ++i) {
- PyObject *pv = PySequence_Fast_GET_ITEM(seq.object(), i);
+ Shiboken::AutoDecRef pv(PySequence_GetItem(seq.object(), i));
values[i] = PyFloat_AsDouble(pv);
}
%PYARG_0 = PyTuple_New(16);
for (Py_ssize_t i = 0; i < 16; ++i) {
PyObject *v = PyFloat_FromDouble(values[i]);
- PyTuple_SET_ITEM(%PYARG_0, i, v);
+ PyTuple_SetItem(%PYARG_0, i, v);
}
// @snippet qmatrix4x4-copydatato
// @snippet qmatrix4x4-mgetitem
if (PySequence_Check(_key)) {
Shiboken::AutoDecRef key(PySequence_Fast(_key, "Invalid matrix index."));
- if (PySequence_Fast_GET_SIZE(key.object()) == 2) {
- PyObject *posx = PySequence_Fast_GET_ITEM(key.object(), 0);
- PyObject *posy = PySequence_Fast_GET_ITEM(key.object(), 1);
+ if (PySequence_Size(key.object()) == 2) {
+ Shiboken::AutoDecRef posx(PySequence_GetItem(key.object(), 0));
+ Shiboken::AutoDecRef posy(PySequence_GetItem(key.object(), 1));
Py_ssize_t x = PyLong_AsSsize_t(posx);
Py_ssize_t y = PyLong_AsSsize_t(posy);
float ret = (*%CPPSELF)(x,y);
{
static int argc;
static char **argv;
- PyObject *stringlist = PyTuple_GET_ITEM(pyargv, 0);
+ PyObject *stringlist = PyTuple_GetItem(pyargv, 0);
if (Shiboken::listToArgcArgv(stringlist, &argc, &argv, "PySideApp")) {
*cptr = new QGuiApplicationWrapper(argc, argv, 0);
Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject *>(self));
float angle{};
%CPPSELF.%FUNCTION_NAME(&outVec, &angle);
%PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[QVector3D](outVec));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[float](angle));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[QVector3D](outVec));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[float](angle));
// @snippet qquaternion-getaxisandangle-vector3d-float
// @snippet qquaternion-geteulerangles
float pitch{}, yaw{}, roll{};
%CPPSELF.%FUNCTION_NAME(&pitch, &yaw, &roll);
%PYARG_0 = PyTuple_New(3);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[float](pitch));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[float](yaw));
-PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[float](roll));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[float](pitch));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[float](yaw));
+PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[float](roll));
// @snippet qquaternion-geteulerangles
// @snippet qregion-len
// @snippet qregion-len
// @snippet qregion-getitem
-if (_i < 0 || _i >= %CPPSELF.rectCount())
- return PyErr_Format(PyExc_IndexError, "index out of bounds");
+const Py_ssize_t size = %CPPSELF.rectCount();
+if (_i < 0 || _i >= size) {
+ Shiboken::Errors::setIndexOutOfBounds(_i, 0, size);
+ return nullptr;
+}
const QRect cppResult = *(%CPPSELF.cbegin() + _i);
return %CONVERTTOPYTHON[QRect](cppResult);
%END_ALLOW_THREADS
QByteArray ba(data, retval);
%PYARG_0 = PyTuple_New(3);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[QByteArray](ba));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QHostAddress](ha));
-PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[quint16](port));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[QByteArray](ba));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QHostAddress](ha));
+PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[quint16](port));
// @snippet qudpsocket-readdatagram
// @snippet qhostinfo-lookuphost-functor
Shiboken::GilState state;
Shiboken::AutoDecRef arglist(PyTuple_New(1));
auto *pyHostInfo = %CONVERTTOPYTHON[QHostInfo](hostInfo);
- PyTuple_SET_ITEM(arglist.object(), 0, pyHostInfo);
+ PyTuple_SetItem(arglist.object(), 0, pyHostInfo);
Shiboken::AutoDecRef ret(PyObject_CallObject(object(), arglist));
release(); // single shot
}
// @snippet qipv6address-getitem
if (_i >= 16) {
PyErr_SetString(PyExc_IndexError, "index out of bounds");
- return 0;
+ return nullptr;
}
if (_i < 0)
_i = 16 - qAbs(_i);
Shiboken::AutoDecRef arglist(PyTuple_New(1));
auto *restReplyPtr = &restReply;
auto *pyRestReply = %CONVERTTOPYTHON[QRestReply*](restReplyPtr);
- PyTuple_SET_ITEM(arglist.object(), 0, pyRestReply);
+ PyTuple_SetItem(arglist.object(), 0, pyRestReply);
Shiboken::AutoDecRef ret(PyObject_CallObject(m_callable, arglist));
Py_DECREF(m_callable);
m_callable = nullptr;
Shiboken::GilState state;
QMultiMap<QString, QVariant> dict = *dictPointer;
Shiboken::AutoDecRef arglist(PyTuple_New(2));
- PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QAbstractOAuth::Stage](stage));
- PyTuple_SET_ITEM(arglist, 1, %CONVERTTOPYTHON[QMultiMap<QString, QVariant>](dict));
+ PyTuple_SetItem(arglist, 0, %CONVERTTOPYTHON[QAbstractOAuth::Stage](stage));
+ PyTuple_SetItem(arglist, 1, %CONVERTTOPYTHON[QMultiMap<QString, QVariant>](dict));
Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
if (!ret.isNull() && PyDict_Check(ret.object()) != 0) {
// @snippet qabstractoauth-setmodifyparametersfunction
%CPPSELF.%FUNCTION_NAME(QAbstractOAuthModifyFunctor(%PYARG_1));
// @snippet qabstractoauth-setmodifyparametersfunction
-
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*********************************************************************
+ * INJECT CODE
+ ********************************************************************/
+
+// @snippet qquick3dinstancing-getinstancebuffer-virtual-redirect
+std::pair<QByteArray, int> resultPair = getInstanceBufferOverride(gil, pyOverride.object());
+if (instanceCount != nullptr)
+ *instanceCount = resultPair.second;
+return resultPair.first;
+// @snippet qquick3dinstancing-getinstancebuffer-virtual-redirect
+
+// @snippet qquick3dinstancing-getinstancebuffer-return
+int count{};
+%RETURN_TYPE retval_ = %CPPSELF.%FUNCTION_NAME(&count);
+%PYARG_0 = PyTuple_New(2);
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[int](count));
+// @snippet qquick3dinstancing-getinstancebuffer-return
// @snippet qcanbus-available-devices
QString errorMessage;
-const QList<QCanBusDeviceInfo> result = %CPPSELF.%FUNCTION_NAME(%1, &errorMessage);
+const QList<QCanBusDeviceInfo> result = %CPPSELF.%FUNCTION_NAME(&errorMessage);
%PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[QList<QCanBusDeviceInfo>](result));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QString](errorMessage));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[QList<QCanBusDeviceInfo>](result));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QString](errorMessage));
// @snippet qcanbus-available-devices
+// @snippet qcanbus-available-devices-plugin
+QString errorMessage;
+const QList<QCanBusDeviceInfo> result = %CPPSELF.%FUNCTION_NAME(%1, &errorMessage);
+%PYARG_0 = PyTuple_New(2);
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[QList<QCanBusDeviceInfo>](result));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QString](errorMessage));
+// @snippet qcanbus-available-devices-plugin
+
// @snippet qcanbus-createdevice
PyObject *pyDevice{};
QString errorMessage;
Py_INCREF(pyDevice);
}
%PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, pyDevice);
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QString](errorMessage));
+PyTuple_SetItem(%PYARG_0, 0, pyDevice);
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QString](errorMessage));
// @snippet qcanbus-createdevice
// http://bugs.openbossa.org/show_bug.cgi?id=362
// PYSIDE-2256: The label was removed
if (!PyObject_TypeCheck(%1, PySideSignalInstance_TypeF()))
- return Shiboken::returnWrongArguments(args, fullName, errInfo);
+ return Shiboken::returnWrongArguments(args, "addTransition", errInfo,
+ SbkPySide6_QtStateMachineTypeStructs[SBK_QAbstractTransition_IDX]);
PySideSignalInstance *signalInstance = reinterpret_cast<PySideSignalInstance *>(%1);
auto sender = %CONVERTTOCPP[QObject *](PySide::Signal::getObject(signalInstance));
QSignalTransition *%0 = %CPPSELF->%FUNCTION_NAME(sender, PySide::Signal::getSignature(signalInstance),%2);
PyErr_SetString(PyExc_ValueError, error.constData());
return -1;
}
-%0 = new QSignalSpyWrapper(emitter, signature.constData());
+%0 = new QSignalSpy(emitter, signature.constData());
// @snippet qsignalspy-signal
qCritical() << "Error while creating the return Tuple";
Py_RETURN_NONE;
}
-PyTuple_SET_ITEM(%PYARG_0, 0, classObj);
-PyTuple_SET_ITEM(%PYARG_0, 1, baseClassObj);
+PyTuple_SetItem(%PYARG_0, 0, classObj);
+PyTuple_SetItem(%PYARG_0, 1, baseClassObj);
// @snippet loaduitype
{
Shiboken::GilState state;
Shiboken::AutoDecRef arglist(PyTuple_New(1));
- PyTuple_SET_ITEM(arglist, 0,
+ PyTuple_SetItem(arglist, 0,
%CONVERTTOPYTHON[QWebEngineCookieStore::FilterRequest](filterRequest));
Shiboken::AutoDecRef ret(PyObject_CallObject(object(), arglist));
return ret.object() == Py_True;
Shiboken::GilState state;
Shiboken::AutoDecRef arglist(PyTuple_New(1));
auto *notification = webEngineNotification.release();
- PyTuple_SET_ITEM(arglist.object(), 0,
+ PyTuple_SetItem(arglist.object(), 0,
%CONVERTTOPYTHON[QWebEngineNotification*](notification));
Shiboken::AutoDecRef ret(PyObject_CallObject(object(), arglist));
};
QString str;
%RETURN_TYPE retval_ = %CPPSELF.%FUNCTION_NAME(%1, %2, %3, &str);
%PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QString](str));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QString](str));
// @snippet qwebenginepage-javascriptprompt-return
+
+// @snippet qwebenginepage-findtext
+auto callable = %PYARG_3;
+auto callback = [callable](const QWebEngineFindTextResult &result)
+{
+ Shiboken::GilState state;
+ Shiboken::AutoDecRef arglist(PyTuple_New(1));
+ PyTuple_SetItem(arglist, 0, %CONVERTTOPYTHON[QWebEngineFindTextResult](result));
+ Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
+ Py_DECREF(callable);
+
+};
+Py_INCREF(callable);
+%CPPSELF.%FUNCTION_NAME(%1, %2, callback);
+// @snippet qwebenginepage-findtext
+
+// @snippet qwebenginepage-print
+auto printer = %PYARG_1;
+auto callable = %PYARG_2;
+auto callback = [printer, callable](bool succeeded)
+{
+ if (!PyCallable_Check(callable)) {
+ qWarning("Argument 2 of %FUNCTION_NAME must be a callable.");
+ return;
+ }
+ Shiboken::GilState state;
+ Shiboken::AutoDecRef arglist(PyTuple_New(1));
+ PyTuple_SetItem(arglist, 0, %CONVERTTOPYTHON[bool](succeeded));
+ Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
+ Py_DECREF(callable);
+ Py_DECREF(printer);
+
+};
+Py_INCREF(printer); // Add a reference to the printer until asynchronous printing has finished
+Py_INCREF(callable);
+%CPPSELF.%FUNCTION_NAME(%1, callback);
+// @snippet qwebenginepage-print
+
+// @snippet qwebenginepage-convertto
+auto callable = %PYARG_1;
+auto callback = [callable](const QString &text)
+{
+ Shiboken::GilState state;
+ Shiboken::AutoDecRef arglist(PyTuple_New(1));
+ PyTuple_SetItem(arglist, 0, %CONVERTTOPYTHON[QString](text));
+ Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
+ Py_DECREF(callable);
+};
+
+Py_INCREF(callable);
+%CPPSELF.%FUNCTION_NAME(callback);
+// @snippet qwebenginepage-convertto
+
+// @snippet qwebenginepage-runjavascript-2
+using RunJavascriptCallback = std::function<void(const QVariant &)>;
+
+if (%PYARG_2 != nullptr && %PYARG_2 != Py_None) {
+ %CPPSELF.%FUNCTION_NAME(%1, RunJavascriptCallback(RunJavascriptFunctor(%PYARG_2)));
+} else {
+ %CPPSELF.%FUNCTION_NAME(%1, 0, RunJavascriptCallback{});
+}
+// @snippet qwebenginepage-runjavascript-2
+
+// @snippet qwebenginepage-runjavascript-3
+using RunJavascriptCallback = std::function<void(const QVariant &)>;
+
+if (%PYARG_3 != nullptr && %PYARG_3 != Py_None) {
+ %CPPSELF.%FUNCTION_NAME(%1, %2, RunJavascriptCallback(RunJavascriptFunctor(%PYARG_3)));
+} else {
+ %CPPSELF.%FUNCTION_NAME(%1, %2, RunJavascriptCallback{});
+}
+// @snippet qwebenginepage-runjavascript-3
+
+// @snippet qwebenginepage-printtopdf
+using PrintToPdfCallback = std::function<void(const QByteArray &)>;
+
+%CPPSELF.%FUNCTION_NAME(PrintToPdfCallback(PrintToPdfFunctor(%PYARG_1)), %2, %3);
+// @snippet qwebenginepage-printtopdf
+
+// @snippet qwebenginepage-findframebyname
+auto frameOptional = %CPPSELF.%FUNCTION_NAME(%1);
+if (frameOptional.has_value()) {
+ const %RETURN_TYPE &frame = frameOptional.value();
+ %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](frame);
+} else {
+ Py_INCREF(Py_None);
+ %PYARG_0 = Py_None;
+}
+// @snippet qwebenginepage-findframebyname
+
+// @snippet qwebengineframe-printtopdf
+using PrintToPdfCallback = std::function<void(const QByteArray &)>;
+
+%CPPSELF.%FUNCTION_NAME(PrintToPdfCallback(PrintToPdfFunctor(%PYARG_1)));
+// @snippet qwebengineframe-printtopdf
+++ /dev/null
-// Copyright (C) 2020 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-// @snippet qwebenginepage-findtext
-auto callable = %PYARG_3;
-auto callback = [callable](const QWebEngineFindTextResult &result)
-{
- if (!PyCallable_Check(callable)) {
- qWarning("Argument 3 of %FUNCTION_NAME must be a callable.");
- return;
- }
- Shiboken::GilState state;
- Shiboken::AutoDecRef arglist(PyTuple_New(1));
- PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QWebEngineFindTextResult](result));
- Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
- Py_DECREF(callable);
-
-};
-Py_INCREF(callable);
-%CPPSELF.%FUNCTION_NAME(%1, %2, callback);
-// @snippet qwebenginepage-findtext
-
-// @snippet qwebenginepage-print
-auto printer = %PYARG_1;
-auto callable = %PYARG_2;
-auto callback = [printer, callable](bool succeeded)
-{
- if (!PyCallable_Check(callable)) {
- qWarning("Argument 2 of %FUNCTION_NAME must be a callable.");
- return;
- }
- Shiboken::GilState state;
- Shiboken::AutoDecRef arglist(PyTuple_New(1));
- PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[bool](succeeded));
- Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
- Py_DECREF(callable);
- Py_DECREF(printer);
-
-};
-Py_INCREF(printer); // Add a reference to the printer until asynchronous printing has finished
-Py_INCREF(callable);
-%CPPSELF.%FUNCTION_NAME(%1, callback);
-// @snippet qwebenginepage-print
-
-// @snippet qwebenginepage-convertto
-auto callable = %PYARG_1;
-auto callback = [callable](const QString &text)
-{
- if (!PyCallable_Check(callable)) {
- qWarning("Argument 1 of %FUNCTION_NAME must be a callable.");
- return;
- }
- Shiboken::GilState state;
- Shiboken::AutoDecRef arglist(PyTuple_New(1));
- PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QString](text));
- Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
- Py_DECREF(callable);
-};
-
-Py_INCREF(callable);
-%CPPSELF.%FUNCTION_NAME(callback);
-// @snippet qwebenginepage-convertto
-
-// @snippet qwebenginepage-runjavascript
-auto callable = %PYARG_3;
-auto callback = [callable](const QVariant &result)
-{
- if (!PyCallable_Check(callable)) {
- qWarning("Argument 3 of %FUNCTION_NAME must be a callable.");
- return;
- }
- Shiboken::GilState state;
- Shiboken::AutoDecRef arglist(PyTuple_New(1));
- switch (result.type()) {
- case QVariant::Bool: {
- const bool value = result.toBool();
- PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QString](value));
- }
- break;
- case QVariant::Int:
- case QVariant::UInt:
- case QVariant::LongLong:
- case QVariant::ULongLong:
- case QVariant::Double: {
- const double number = result.toDouble();
- PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[double](number));
- }
- break;
- default: {
- const QString value = result.toString();
- PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QString](value));
- }
- break;
- }
- // PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[bool](found));
- Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
- Py_DECREF(callable);
-};
-
-Py_INCREF(callable);
-%CPPSELF.%FUNCTION_NAME(%1, %2, callback);
-// @snippet qwebenginepage-runjavascript
-
-// @snippet qwebenginepage-printtopdf
-auto callable = %PYARG_1;
-auto callback = [callable](const QByteArray &pdf)
-{
- if (!PyCallable_Check(callable)) {
- qWarning("Argument 1 of %FUNCTION_NAME must be a callable.");
- return;
- }
- Shiboken::GilState state;
- Shiboken::AutoDecRef arglist(PyTuple_New(1));
- PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QByteArray](pdf));
- Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
- Py_DECREF(callable);
-};
-
-Py_INCREF(callable);
-%CPPSELF.%FUNCTION_NAME(callback, %2);
-// @snippet qwebenginepage-printtopdf
QGraphicsItem *item_ = nullptr;
%RETURN_TYPE retval_ = %CPPSELF.%FUNCTION_NAME(&item_);
%PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QGraphicsItem *](item_));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QGraphicsItem *](item_));
// @snippet qgraphicsitem-isblockedbymodalpanel
// @snippet qitemeditorfactory-registereditor
QFormLayout::ItemRole _role;
%CPPSELF->%FUNCTION_NAME(%ARGUMENT_NAMES, &_row, &_role);
%PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[int](_row));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[int](_row));
// On the C++ side, *rolePtr is not set if row == -1, in which case on
// the Python side this gets converted to a random value outside the
// enum range. Fix this by setting _role to a default value here.
if (_row == -1)
_role = QFormLayout::LabelRole;
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QFormLayout::ItemRole](_role));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QFormLayout::ItemRole](_role));
// @snippet qformlayout-fix-args
// @snippet qfiledialog-return
%RETURN_TYPE retval_ = %CPPSELF.%FUNCTION_NAME(%1, %2, %3, %4, &%5, %6);
%END_ALLOW_THREADS
%PYARG_0 = PyTuple_New(2);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QString](%5));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QString](%5));
// @snippet qfiledialog-return
// @snippet qwidget-addaction-glue
int a, b, c, d;
%CPPSELF.%FUNCTION_NAME(%1, &a, &b, &c, &d);
%PYARG_0 = PyTuple_New(4);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[int](a));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[int](b));
-PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[int](c));
-PyTuple_SET_ITEM(%PYARG_0, 3, %CONVERTTOPYTHON[int](d));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[int](a));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[int](b));
+PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[int](c));
+PyTuple_SetItem(%PYARG_0, 3, %CONVERTTOPYTHON[int](d));
// @snippet qgridlayout-getitemposition
// @snippet qgraphicsscene-destroyitemgroup
{
static int argc;
static char **argv;
- PyObject *stringlist = PyTuple_GET_ITEM(pyargv, 0);
+ PyObject *stringlist = PyTuple_GetItem(pyargv, 0);
if (Shiboken::listToArgcArgv(stringlist, &argc, &argv, "PySideApp")) {
*cptr = new QApplicationWrapper(argc, argv, 0);
Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject *>(self));
Shiboken::Object::removeParent(reinterpret_cast<SbkObject *>(%PYSELF));
// @snippet qdialog-exec-remove-parent-relation
+// @snippet qmessagebox-open-connect-accept
+if (!PySide::callConnect(%PYSELF, SIGNAL(accepted()), %PYARG_1))
+ return nullptr;
+%CPPSELF.%FUNCTION_NAME();
+// @snippet qmessagebox-open-connect-accept
+
/*********************************************************************
* CONVERSIONS
********************************************************************/
&_errorColumn_);
%END_ALLOW_THREADS
%PYARG_0 = PyTuple_New(4);
-PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[bool](_ret_));
-PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[QString](_errorMsg_));
-PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[int](_errorLine_));
-PyTuple_SET_ITEM(%PYARG_0, 3, %CONVERTTOPYTHON[int](_errorColumn_));
+PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[bool](_ret_));
+PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[QString](_errorMsg_));
+PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[int](_errorLine_));
+PyTuple_SetItem(%PYARG_0, 3, %CONVERTTOPYTHON[int](_errorColumn_));
// @snippet qdomdocument-setcontent
QT_END_NAMESPACE
#endif
-
-
#ifndef QTCOREHELPER_H
#define QTCOREHELPER_H
+#include <QtCore/qdirlisting.h>
#include <QtCore/qmutex.h>
#include <QtCore/qobjectdefs.h>
std::shared_ptr<QGenericReturnArgumentData> d;
};
+ struct QDirListingIteratorPrivate;
+
+ class QDirListingIterator
+ {
+ public:
+ explicit QDirListingIterator(const QDirListing &dl);
+ QDirListingIterator();
+
+ QDirListingIterator(const QDirListingIterator &);
+ QDirListingIterator &operator=(const QDirListingIterator &);
+ QDirListingIterator(QDirListingIterator &&) noexcept;
+ QDirListingIterator &operator=(QDirListingIterator &&) noexcept;
+ ~QDirListingIterator();
+
+ bool next();
+ const QDirListing::DirEntry &value() const;
+ bool atEnd() const;
+
+ private:
+ std::shared_ptr<QDirListingIteratorPrivate> d;
+ };
+
} // namespace QtCoreHelper
QT_END_NAMESPACE
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
from shiboken6.Shiboken import VoidPtr
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
deprecated.py
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
generate_pyi.py
This script generates the .pyi files for all PySide modules.
"""
+# mypy: disable-error-code="import-not-found"
import argparse
import inspect # noqa: F401
</template>
<template name="pybytes_const_uchar">
- const uchar *%out = reinterpret_cast<const uchar*>(PyBytes_AS_STRING(%PYARG_1));
+ const uchar *%out = reinterpret_cast<const uchar*>(PyBytes_AsString(%PYARG_1));
</template>
<template name="pybytes_uint">
<template name="tuple_ok_retval">
%PYARG_0 = PyTuple_New(2);
- PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[bool](ok_));
- PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+ PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[bool](ok_));
+ PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
</template>
<template name="bool*_fix,arg">
<!-- Templates to fix bool* parameters -->
<template name="tuple_retval_ok">
%PYARG_0 = PyTuple_New(2);
- PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
- PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[bool](ok_));
+ PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+ PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[bool](ok_));
</template>
<template name="fix_bool*">
char val_{};
%RETURN_TYPE retval_ = %CPPSELF.%FUNCTION_NAME(&val_);
%PYARG_0 = PyTuple_New(2);
- PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
- PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[char](val_));
+ PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+ PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[char](val_));
</template>
<template name="tuple_abcd_same_type">
%PYARG_0 = PyTuple_New(4);
- PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[$TYPE](a));
- PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[$TYPE](b));
- PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[$TYPE](c));
- PyTuple_SET_ITEM(%PYARG_0, 3, %CONVERTTOPYTHON[$TYPE](d));
+ PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[$TYPE](a));
+ PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[$TYPE](b));
+ PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[$TYPE](c));
+ PyTuple_SetItem(%PYARG_0, 3, %CONVERTTOPYTHON[$TYPE](d));
</template>
<template name="fix_number*,number*,number*,number*">
<template name="fix_native_return_number*,number*,number*,number*">
PyObject* _obj = %PYARG_0.object();
+ Shiboken::AutoDecRef _obj0(PySequence_GetItem(_obj, 0));
+ Shiboken::AutoDecRef _obj1(PySequence_GetItem(_obj, 1));
+ Shiboken::AutoDecRef _obj2(PySequence_GetItem(_obj, 2));
+ Shiboken::AutoDecRef _obj3(PySequence_GetItem(_obj, 3));
if (!PySequence_Check(_obj)
- || PySequence_Fast_GET_SIZE(_obj) != 4
- || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 0))
- || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 1))
- || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 2))
- || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 3))) {
+ || PySequence_Size(_obj) != 4
+ || !PyNumber_Check(_obj0)
+ || !PyNumber_Check(_obj1)
+ || !PyNumber_Check(_obj2)
+ || !PyNumber_Check(_obj3)) {
PyErr_SetString(PyExc_TypeError, "Sequence of 4 numbers expected");
} else {
- *%1 = %CONVERTTOCPP[$TYPE](PySequence_Fast_GET_ITEM(_obj, 0));
- *%2 = %CONVERTTOCPP[$TYPE](PySequence_Fast_GET_ITEM(_obj, 1));
- *%3 = %CONVERTTOCPP[$TYPE](PySequence_Fast_GET_ITEM(_obj, 2));
- *%4 = %CONVERTTOCPP[$TYPE](PySequence_Fast_GET_ITEM(_obj, 3));
+ *%1 = %CONVERTTOCPP[$TYPE](_obj0);
+ *%2 = %CONVERTTOCPP[$TYPE](_obj1);
+ *%3 = %CONVERTTOCPP[$TYPE](_obj2);
+ *%4 = %CONVERTTOCPP[$TYPE](_obj3);
}
</template>
$TYPE a, b, c, d, e;
%CPPSELF.%FUNCTION_NAME(&a, &b, &c, &d, &e);
%PYARG_0 = PyTuple_New(5);
- PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[$TYPE](a));
- PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[$TYPE](b));
- PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[$TYPE](c));
- PyTuple_SET_ITEM(%PYARG_0, 3, %CONVERTTOPYTHON[$TYPE](d));
- PyTuple_SET_ITEM(%PYARG_0, 4, %CONVERTTOPYTHON[$TYPE](e));
+ PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[$TYPE](a));
+ PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[$TYPE](b));
+ PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[$TYPE](c));
+ PyTuple_SetItem(%PYARG_0, 3, %CONVERTTOPYTHON[$TYPE](d));
+ PyTuple_SetItem(%PYARG_0, 4, %CONVERTTOPYTHON[$TYPE](e));
</template>
<template name="fix_args,number*,number*">
$TYPE a, b;
%CPPSELF.%FUNCTION_NAME(%ARGUMENT_NAMES, &a, &b);
%PYARG_0 = PyTuple_New(2);
- PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[$TYPE](a));
- PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[$TYPE](b));
+ PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[$TYPE](a));
+ PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[$TYPE](b));
</template>
<template name="fix_arg,int*,int*">
int a, b;
_ret = %CPPSELF.%FUNCTION_NAME(%1, &a, &b);
%PYARG_0 = PyTuple_New(3);
- PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](_ret));
- PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[int](a));
- PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[int](b));
+ PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](_ret));
+ PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[int](a));
+ PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[int](b));
</template>
<template name="return_tuple_QValidator_QString_int">
%RETURN_TYPE retval_ = %RETURN_TYPE(%CPPSELF.%FUNCTION_NAME(%1, %2));
%PYARG_0 = PyTuple_New(3);
- PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
- PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
- PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[%ARG2_TYPE](%2));
+ PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+ PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
+ PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[%ARG2_TYPE](%2));
</template>
<template name="repr_code">
<template name="return_native_eventfilter">
%PYARG_0 = PyTuple_New(2);
- PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](%0));
- PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[long](*result_out));
+ PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](%0));
+ PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[long](*result_out));
</template>
<!-- templates for __reduce__ -->
<template name="load_xpm">
Shiboken::AutoDecRef strList(PySequence_Fast(%PYARG_1, "Invalid sequence."));
- Py_ssize_t lineCount = PySequence_Fast_GET_SIZE(strList.object());
+ Py_ssize_t lineCount = PySequence_Size(strList.object());
for (Py_ssize_t line = 0; line < lineCount; ++line) {
- if (!Shiboken::String::check(PySequence_Fast_GET_ITEM(strList.object(), line))) {
+ Shiboken::AutoDecRef _obj(PySequence_GetItem(strList.object(), line));
+ bool isString = Shiboken::String::check(_obj);
+ if (!isString) {
PyErr_SetString(PyExc_TypeError, "The argument must be a sequence of strings.");
break;
}
break;
auto xpm = new const char*[lineCount];
- for (Py_ssize_t line = 0; line < lineCount; ++line)
- xpm[line] = Shiboken::String::toCString(PySequence_Fast_GET_ITEM(strList.object(), line));
+ for (Py_ssize_t line = 0; line < lineCount; ++line) {
+ Shiboken::AutoDecRef _obj(PySequence_GetItem(strList.object(), line));
+ xpm[line] = Shiboken::String::toCString(_obj);
+ }
%0 = new %TYPE(xpm);
%ARG1_TYPE a, b;
%CPPSELF.%FUNCTION_NAME(%1, %2, &a, &b);
%PYARG_0 = PyTuple_New(2);
- PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%ARG1_TYPE](a));
- PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG1_TYPE](b));
+ PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%ARG1_TYPE](a));
+ PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG1_TYPE](b));
</template>
<template name="qimage_buffer_constructor">
if (PySequence_Check(%PYARG_0)) {
Shiboken::AutoDecRef seq(PySequence_Fast(%PYARG_0, 0));
- const Py_ssize_t size = PySequence_Fast_GET_SIZE(seq.object());
+ const Py_ssize_t size = PySequence_Size(seq.object());
if (size > 1) {
- if (%ISCONVERTIBLE[QString](PySequence_Fast_GET_ITEM(seq.object(), 1)))
- %1 = %CONVERTTOCPP[QString](PySequence_Fast_GET_ITEM(seq.object(), 1));
+ Shiboken::AutoDecRef _obj1(PySequence_GetItem(seq.object(), 1));
+ if (%ISCONVERTIBLE[QString](_obj1))
+ %1 = %CONVERTTOCPP[QString](_obj1);
else
qWarning("%TYPE::%FUNCTION_NAME: Second tuple element is not convertible to unicode.");
}
if (size > 2) {
- if (%ISCONVERTIBLE[int](PySequence_Fast_GET_ITEM(seq.object(), 2)))
- %2 = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(seq.object(), 2));
+ Shiboken::AutoDecRef _obj2(PySequence_GetItem(seq.object(), 2));
+ if (%ISCONVERTIBLE[int](_obj2))
+ %2 = %CONVERTTOCPP[int](_obj2);
else
qWarning("%TYPE::%FUNCTION_NAME: Second tuple element is not convertible to int.");
}
- %PYARG_0.reset(PySequence_Fast_GET_ITEM(seq.object(), 0));
+ Shiboken::AutoDecRef _sobj(PySequence_GetItem(seq.object(), 0));
+
+ %PYARG_0.reset(_sobj);
Py_INCREF(%PYARG_0); // we need to incref, because "%PYARG_0 = ..." will decref the tuple and the tuple will be decrefed again at the end of this scope.
}
PyObject *pyData = PyTuple_New(%MATRIX_SIZE);
if (data) {
for(int i=0; i < %MATRIX_SIZE; i++)
- PyTuple_SET_ITEM(pyData, i, %CONVERTTOPYTHON[float](data[i]));
+ PyTuple_SetItem(pyData, i, %CONVERTTOPYTHON[float](data[i]));
}
return pyData;
</template>
"Failed to parse sequence on %TYPE constructor."));
float values[%SIZE];
for(int i=0; i < %SIZE; i++) {
- PyObject *pv = PySequence_Fast_GET_ITEM(fast.object(), i);
+ Shiboken::AutoDecRef pv(PySequence_GetItem(fast.object(), i));
values[i] = %CONVERTTOCPP[float](pv);
}
%0 = new %TYPE(values);
</template>
<template name="pybytes_const_uchar">
- const uchar *%out = reinterpret_cast<const uchar*>(PyBytes_AS_STRING(%PYARG_1));
+ const uchar *%out = reinterpret_cast<const uchar*>(PyBytes_AsString(%PYARG_1));
</template>
<template name="pybytes_uint">
Shiboken::AutoDecRef fast(PySequence_Fast(%PYARG_2,
"Failed to parse sequence with type %VECTOR_TYPE."));
for(Py_ssize_t i=0; i < _size; i++) {
- PyObject* pv = PySequence_Fast_GET_ITEM(fast.object(), i);
+ Shiboken::AutoDecRef pv(PySequence_GetItem(fast.object(), i));
_list[i] = %CONVERTTOCPP[$ATTR_TYPE](pv);
}
}
const Py_ssize_t numItems = PySequence_Size(%PYARG_1);
Shiboken::AutoArrayPointer<QGraphicsItem*> %out(numItems);
for (Py_ssize_t i = 0; i < numItems; ++i) {
- %out[i] = %CONVERTTOCPP[QGraphicsItem*](PySequence_Fast_GET_ITEM(%PYARG_1, i));
+ Shiboken::AutoDecRef _arg1(PySequence_GetItem(%PYARG_1, i));
+ %out[i] = %CONVERTTOCPP[QGraphicsItem*](_arg1);
}
</template>
const Py_ssize_t numOptions = PySequence_Size(%PYARG_2);
Shiboken::AutoArrayPointer<QStyleOptionGraphicsItem> %out(numOptions);
for (Py_ssize_t i=0; i < numOptions; ++i) {
- %out[i] = %CONVERTTOCPP[QStyleOptionGraphicsItem](PySequence_Fast_GET_ITEM(%PYARG_1, i));
+ Shiboken::AutoDecRef _arg1(PySequence_GetItem(%PYARG_1, i));
+ %out[i] = %CONVERTTOCPP[QStyleOptionGraphicsItem](_arg1);
}
</template>
<template name="pysequencesize_int">
- int %out = PySequence_Size(%PYARG_1);
+ Py_ssize_t %out = PySequence_Size(%PYARG_1);
</template>
</typesystem>
list(APPEND shiboken_include_dir_list ${${module_ADDITIONAL_INCLUDE_DIRS}})
endif()
- # Transform the path separators into something shiboken understands.
+ # Remove duplicates and transform the path separators into something
+ # shiboken understands.
+ list(REMOVE_DUPLICATES shiboken_include_dir_list)
make_path(shiboken_include_dirs ${shiboken_include_dir_list})
set(force_process_system_include_paths_list "")
set(${module_NAME}_glue_dependency ${${module_NAME}_glue})
endif()
+ # Install module doc files.
+ set(${module_NAME}_doc "${CMAKE_CURRENT_SOURCE_DIR}/../doc/${lower_module_name}.rst")
+ set(${module_name}_doc_dependency "")
+ if(EXISTS ${${module_NAME}_doc})
+ install(FILES ${${module_NAME}_doc} DESTINATION share/PySide6${pyside6_SUFFIX}/doc)
+ set(${module_NAME}_doc_dependency ${${module_NAME}_doc})
+ endif()
+
# Install standalone glue files into typesystems subfolder, so that the resolved relative
# paths remain correct.
if (module_GLUE_SOURCES)
--lean-headers
--api-version=${SUPPORTED_QT_VERSION})
+ # check if building for Android with a macOS host
+ # This is not needed for Linux because OpenGLES2 development binaries in
+ # linux can be installed by installing 'libgles2-mesa-dev' package which
+ # comes as a default requirement for building PySide6. As such for
+ # cross-compiling in linux, we use the clang compiler from the installed
+ # libclang itself.
+ if(CMAKE_ANDROID_ARCH_LLVM_TRIPLE AND CMAKE_HOST_APPLE)
+ message(STATUS "Building for Android with arch ${CMAKE_ANDROID_ARCH_LLVM_TRIPLE}")
+ list(APPEND shiboken_command "--clang-option=--target=${CMAKE_ANDROID_ARCH_LLVM_TRIPLE}")
+
+ # CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX does not contain the ANDROID_PLATFORM i.e. it ends with
+ # the form 'aarch64-linux-android-'. Remove the last '-' and add the corresponding clang
+ # based on ANDROID_PLATFORM making it 'aarch64-linux-android26-clang++'
+
+ # Get the length of the string
+ string(LENGTH "${CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX}" _length)
+
+ # Subtract 1 from the length to get the characters till '-'
+ math(EXPR _last_index "${_length} - 1")
+
+ # Get the substring from the start to the character before the last one
+ string(SUBSTRING "${CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX}" 0 "${_last_index}"
+ SHIBOKEN_ANDROID_COMPILER_PREFIX)
+
+ # use the compiler from the Android NDK
+ list(APPEND shiboken_command
+ "--compiler-path=${SHIBOKEN_ANDROID_COMPILER_PREFIX}${CMAKE_ANDROID_API}-clang++")
+ endif()
+
if(CMAKE_HOST_APPLE)
set(shiboken_framework_include_dir_list ${QT_FRAMEWORK_INCLUDE_DIR})
make_path(shiboken_framework_include_dirs ${shiboken_framework_include_dir_list})
DEPENDS ${total_type_system_files}
${module_GLUE_SOURCES}
${${module_NAME}_glue_dependency}
+ ${${module_NAME}_doc_dependency}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Running generator for ${module_NAME}...")
${generate_pyi_options})
add_dependencies("${module_NAME}_pyi" ${module_NAME})
+ if(TARGET shibokenmodule)
+ add_dependencies("${module_NAME}_pyi" shibokenmodule)
+ endif()
+
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/../${module_NAME}.pyi"
DESTINATION "${PYTHON_SITE_PACKAGES}/PySide6")
endif()
set("end_${name}" "-->" PARENT_SCOPE)
endif()
endmacro()
-
SvgWidgets
DataVisualization
Graphs
+ GraphsWidgets
Bluetooth)
find_package(Qt${QT_MAJOR_VERSION}UiTools)
if(Qt${QT_MAJOR_VERSION}UiTools_FOUND)
endif()
list(APPEND ALL_OPTIONAL_MODULES WebChannel WebEngineCore WebEngineWidgets
WebEngineQuick WebSockets HttpServer)
+ find_package(Qt${QT_MAJOR_VERSION}WebEngineQuick)
+ # for Windows and Linux, QtWebView depends on QtWebEngine to render content
+ if(Qt${QT_MAJOR_VERSION}WebEngineQuick_FOUND OR APPLE)
+ list(APPEND ALL_OPTIONAL_MODULES WebView)
+ endif()
list(APPEND ALL_OPTIONAL_MODULES 3DCore 3DRender 3DInput 3DLogic 3DAnimation 3DExtras)
endmacro()
set(ENABLE_MAC "0")
set(ENABLE_WIN "0")
- if(CMAKE_HOST_APPLE)
- set(ENABLE_MAC "1")
- elseif(CMAKE_HOST_WIN32)
- set(ENABLE_WIN "1")
- set(ENABLE_UNIX "0")
- elseif(NOT CMAKE_HOST_UNIX)
- message(FATAL_ERROR "OS not supported")
+ # check if Android, if so, set ENABLE_UNIX=1
+ # this is needed to avoid including the wrapper specific to macOS when building for Android
+ # from a macOS host
+ if(NOT CMAKE_SYSTEM_NAME STREQUAL "Android")
+ if(CMAKE_HOST_APPLE)
+ set(ENABLE_MAC "1")
+ elseif(CMAKE_HOST_WIN32)
+ set(ENABLE_WIN "1")
+ set(ENABLE_UNIX "0")
+ elseif(NOT CMAKE_HOST_UNIX)
+ message(FATAL_ERROR "OS not supported")
+ endif()
endif()
endmacro()
list(REMOVE_ITEM DISABLED_MODULES ${m})
endif()
endforeach()
+
+ # Special list of modules for the __init__.py.in file
+ foreach(im ${all_module_shortnames})
+ list(APPEND init_modules "\"Qt${im}\"")
+ endforeach()
+ string(REPLACE ";" ", " init_modules "${init_modules}")
endmacro()
macro(collect_module_if_found shortname)
# If the module was found, and also the module path is the same as the
# Qt5Core base path, we will generate the list with the modules to be installed
set(looked_in_message ". Looked in: ${${_name_dir}}")
- if("${${_name_found}}" AND (("${found_basepath}" GREATER "0") OR ("${found_basepath}" EQUAL "0")))
+
+ # 'found_basepath' is used to ensure consistency that all the modules are from the same Qt
+ # directory which prevents issues from arising due to mixing versions or using incompatible Qt
+ # modules. When SHIBOKEN_FORCE_PROCESS_SYSTEM_INCLUDE_PATHS is not empty, we can ignore this
+ # requirement of 'found_basepath'.
+ # This is specifically useful for Flatpak build of PySide6 where For Flatpak the modules are in
+ # different directories. For Flatpak, although the modules are in different directories, they
+ # are all compatible.
+ if("${${_name_found}}" AND
+ ((("${found_basepath}" GREATER "0") OR ("${found_basepath}" EQUAL "0")) OR
+ (NOT SHIBOKEN_FORCE_PROCESS_SYSTEM_INCLUDE_PATHS STREQUAL "")))
message(STATUS "${module_state} module ${name} found (${ARGN})${looked_in_message}")
# record the shortnames for the tests
list(APPEND all_module_shortnames ${shortname})
endif()
shiboken_get_debug_level(debug_level)
+if(SHIBOKEN_UNOPTIMIZE)
+ SET(UNOPTIMIZE "--unoptimize=${SHIBOKEN_UNOPTIMIZE}")
+ENDIF()
set(GENERATOR_EXTRA_FLAGS
${SHIBOKEN_GENERATOR_EXTRA_FLAGS}
${debug_level}
+ ${UNOPTIMIZE}
--generator-set=shiboken
--enable-parent-ctor-heuristic
--enable-pyside-extensions
endif()
find_package(Qt6 COMPONENTS Designer)
-
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
COMMENT "Generating example gallery...")
+# Generate the release notes
+set(RELEASED_NOTES_TARGET "${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR}/release_notes")
+set(RELEASE_NOTES_OPTIONS --target "${RELEASED_NOTES_TARGET}")
+set(RELEASE_NOTES_TOOL "${TOOLS_DIR}/release_notes/main.py")
+if (NOT QUIET_BUILD)
+ list(APPEND RELEASE_NOTES_OPTIONS "-v")
+endif()
+
+add_custom_target("release_notes" DEPENDS "${RELEASED_NOTES_TARGET}")
+add_custom_command(OUTPUT "${RELEASED_NOTES_TARGET}"
+ COMMAND ${python_executable} ${RELEASE_NOTES_TOOL} ${RELEASE_NOTES_OPTIONS}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
+ COMMENT "Generating release notes...")
+
set(SHIBOKEN_INTERSPHINX_FILE "${ROOT}/pyside6/shiboken6/objects.inv")
set(HAS_WEBENGINE_WIDGETS 0)
set(SKIP_SPHINX_WARNINGS 1)
if (FULLDOCSBUILD)
set(SKIP_SPHINX_WARNINGS 0)
- set(SHIBOKEN_INTERSPHINX_FILE "${CMAKE_BINARY_DIR}/doc/html/shiboken6/doc/html/objects.inv")
+ set(SHIBOKEN_INTERSPHINX_FILE "${CMAKE_BINARY_DIR}/doc/html/shiboken6/objects.inv")
# For Qt modules that are part of the documentation build:
# - Configure the module docconf file
# - Write shiboken header consisting of pyside6_global.h and module includes
--api-version=${SUPPORTED_QT_VERSION}
--typesystem-paths="${QDOC_TYPESYSTEM_PATH}"
--library-source-dir=${QT_SRC_DIR}
- --documentation-data-dir=${DOC_DATA_DIR}/webxml
+ --documentation-data-dir=${DOC_DATA_DIR}
--output-directory=${CMAKE_CURRENT_BINARY_DIR}/${DOC_BASE_DIR}
--documentation-code-snippets-dir=${CODE_SNIPPET_ROOT}
--snippets-path-rewrite=${QT_ROOT_PATH}:${CODE_SNIPPET_ROOT}
COMMENT "Creating 3rdparty license documentation..."
)
-add_dependencies(docrsts example_gallery)
+add_dependencies(docrsts example_gallery release_notes)
if (FULLDOCSBUILD)
add_dependencies(apidoc docrsts licensedocrsts)
to run the coroutine and then stop the event loop upon its completion.
This latter case behaves identically to ``asyncio.run(my_coroutine())``.
+If there is no instance of a QCoreApplication, QGuiApplication or
+QApplication yet, a new instance of QCoreApplication is created.
+
An additional optional argument ``quit_qapp`` can be passed to ``run()``
to configure whether the QCoreApplication at the core of QtAsyncio
should be shut down when asyncio finishes. A special case where one
Coroutines are functions that can be paused (yield) and resumed. Behind
this simple concept lies a complex mechanism that is abstracted by the
-asynchronous framework. This talk presents a diagram that attempts to
-illustrate the flow of a coroutine from the moment it's provided to the
-async framework until it's completed.
+asynchronous framework. `This talk <https://www.youtube.com/watch?v=XuqdTvisqkQ>`_
+presents the below diagram that attempts to illustrate the flow of a
+coroutine from the moment it's provided to the async framework until
+it's completed.
-.. image:: https://img.youtube.com/vi/XuqdTvisqkQ/mqdefault.jpg
- :alt: Asynchronous programming with asyncio and Qt
- :target: https://www.youtube.com/watch?v=XuqdTvisqkQ
+.. image:: coroutines.png
+ :alt: Coroutines explained
font-size: +1rem;
color: var(--color-brand-primary);
}
+
+article[role="main"] section[id^="pyside6-"] h1 {
+ font-size: 1.5em;
+}
+
+article[role="main"] section[id^="pyside6-"] dl dt em span.pre{
+ font-size: 1.5em;
+}
+
+article[role="main"] section[id^="pyside6-"] dl dt span.pre {
+ font-size: 1.5em;
+}
+
+.prominent-summary {
+ font-size: 1em;
+ font-weight: bold;
+ color: #2a9d8f;
+ background-color: #f0f0f0;
+ padding: 5px;
+ border-radius: 5px;
+}
+
+section#examples .sd-card {
+ transition: transform 0.2s, box-shadow 0.2s;
+}
+
+section#examples .sd-card-title {
+ font-size: 1.25em;
+ font-weight: bold;
+}
+
+section#examples .sd-card-body{
+ font-size: 1em;
+}
+
+/* Example card hover */
+section#examples .cover-img img:hover {
+ background-color: var(--color-background);
+ opacity: 0.3;
+}
+
+section#examples .sd-card {
+ z-index: 1;
+ position: relative;
+}
+
+section#examples .sd-card-hover:hover {
+ background-color: var(--color-background);
+ transform: translateY(-5px);
+ box-shadow: 0 4px 8px rgba(27, 31, 35, 0.12), 0 12px 48px rgba(27, 31, 35, 0.24);
+}
+
+section#examples .sd-card-hover:hover .sd-card-footer {
+ bottom: 0;
+ display: block;
+ left: 0;
+ position: absolute;
+ color: var(--color-background-font);
+ right: 0;
+ top: 0;
+ padding: 20px;
+ opacity: 1;
+ z-index: 5;
+ background-color: var(--color-background);
+}
+
+section#examples .sd-card-hover:hover a {
+ z-index: 7;
+}
+
+section#examples details .sd-card .sd-card-footer {
+ display: none;
+}
+
+section#examples details .sd-card-body p strong {
+ display: block;
+ font-size: 1.5em;
+}
+
+section#examples details .sd-card-title {
+ font-size: 1.1em;
+}
+.. _sphx_tag_android:
+
My tags: Android
################
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-# see README.md
# A line enclosed in [] denotes a (relative) target directory
[overviews]
-animation-overview.webxml
-animation.webxml
-application-windows.webxml
-audiooverview.webxml
-bindableproperties.webxml
-bluetooth-examples.webxml
-cameraoverview.webxml
-compatmap.webxml
-concurrent-changes-qt6.webxml
-containers.webxml
-coordsys.webxml
-creating-a-sensor-plugin.webxml
-custom-types.webxml
-database.webxml
-datastreamformat.webxml
-dbus-changes-qt6.webxml
-determining-the-default-sensor-for-a-type.webxml
-dialogs.webxml
-dnd.webxml
-dynamic-sensor-backend-registration.webxml
-echoclient.webxml
-echoserver.webxml
-events.webxml
-eventsandfilters.webxml
-examples-dbus.webxml
-examples-designer.webxml
-examples-desktop.webxml
-examples-dialogs.webxml
-examples-graphicsview.webxml
-examples-itemviews.webxml
-examples-mainwindow.webxml
-examples-network.webxml
-examples-painting.webxml
-examples-qthelp.webxml
-examples-qtnetworkauth.webxml
-examples-qtscxml.webxml
-examples-qtuitools.webxml
-examples-richtext.webxml
-examples-widgets-opengl.webxml
-examples-widgets.webxml
-focus.webxml
-gallery.webxml
-genericbackend.webxml
-gestures-overview.webxml
-graphicsview.webxml
-gui-changes-qt6.webxml
-helpsystem.webxml
-implicit-sharing.webxml
-io-functions.webxml
-io.webxml
-java-style-iterators.webxml
-json.webxml
-layout.webxml
-mainwindow.webxml
-metaobjects.webxml
-model-view-programming.webxml
-modelview.webxml
-multimedia-examples.webxml
-multimediaoverview.webxml
-network-changes-qt6.webxml
-network.webxml
-nfc-android.webxml
-nfc-examples.webxml
-object.webxml
-objecttrees.webxml
-opengl-changes-qt6.webxml
-painting-3d.webxml
-painting.webxml
-paintsystem-devices.webxml
-paintsystem-drawing.webxml
-paintsystem-images.webxml
-paintsystem.webxml
-plugins.webxml
-position-plugin-geoclue2.webxml
-position-plugin-gypsy.webxml
-position-plugin-nmea.webxml
-positioning-cpp-qml.webxml
-printing.webxml
-printsupport-changes-qt6.webxml
-properties.webxml
-qabstracteventdispatcher-timerinfo.webxml
-qabstracttextdocumentlayout-paintcontext.webxml
-qabstracttextdocumentlayout-selection.webxml
-qaccessible-state.webxml
-qadoptshareddatatag.webxml
-qcborerror.webxml
-qcborparsererror.webxml
-qcborstreamreader-stringresult.webxml
-qdbusdeclaringsignals.webxml
-qdbusdeclaringslots.webxml
-qdbustypesystem.webxml
-qdbusviewer.webxml
-qdbusxml2cpp.webxml
-qdrawutil-h.webxml
-qdtlsclientverifier-generatorparameters.webxml
-qformlayout-takerowresult.webxml
-qglobalstatic.webxml
-qhashseed.webxml
-qhelplink.webxml
-qiconengine-scaledpixmapargument.webxml
-qiterator.webxml
-qjsonparseerror.webxml
-qmediatimerange-interval.webxml
-qml-advtutorial.webxml
-qml-changes-qt6.webxml
-qml-dynamicview-tutorial.webxml
-qml-tutorial.webxml
-qml-tutorial1.webxml
-qml-tutorial2.webxml
-qml-tutorial3.webxml
-qmldiskcache.webxml
-qmlreference.webxml
-qndeffilter-record.webxml
-qoutputrange.webxml
-qpageranges-range.webxml
-qprocess-createprocessarguments.webxml
-qqmlcontext-propertypair.webxml
-qquickitem-itemchangedata.webxml
-qscxmlc.webxml
-qsggeometry-attribute.webxml
-qsggeometry-coloredpoint2d.webxml
-qsggeometry-point2d.webxml
-qsggeometry-texturedpoint2d.webxml
-qsgmaterialshader-graphicspipelinestate.webxml
-qsgmaterialtype.webxml
-qstaticplugin.webxml
-qt3d-basicshapes-cpp-example.webxml
-qt3d-changes-qt6.webxml
-qt3d-cpp.webxml
-qt3d-examples.webxml
-qt3d-multiviewport-example.webxml
-qt3d-overview.webxml
-qt3d-pbr-materials-example.webxml
-qt3d-qml.webxml
-qt3d-simple-qml-example.webxml
-qt3d-simplecustommaterial-example.webxml
-qt3d-wireframe-example.webxml
-qt3drender-framegraph.webxml
-qt3drender-geometry.webxml
-qt3drender-porting-to-rhi.webxml
-qt3drender-protips.webxml
-qtalgorithms.webxml
-qtbluetooth-btchat-example.webxml
-qtbluetooth-changes-qt6.webxml
-qtbluetooth-le-overview.webxml
-qtbluetooth-lowenergyscanner-example.webxml
-qtbluetooth-overview.webxml
-qtcborcommon.webxml
-qtcharts-barmodelmapper-example.webxml
-qtcharts-boxplotchart-example.webxml
-qtcharts-candlestickchart-example.webxml
-qtcharts-changes-qt6.webxml
-qtcharts-customchart-example.webxml
-qtcharts-datetimeaxis-example.webxml
-qtcharts-examples.webxml
-qtcharts-horizontalbarchart-example.webxml
-qtcharts-horizontalpercentbarchart-example.webxml
-qtcharts-horizontalstackedbarchart-example.webxml
-qtcharts-legendmarkers-example.webxml
-qtcharts-multiaxis-example.webxml
-qtcharts-openglseries-example.webxml
-qtcharts-overview.webxml
-qtcharts-pointsselectionandmarkers-example.webxml
-qtcharts-polarchart-example.webxml
-qtcharts-qmlaxes-example.webxml
-qtcharts-qmlcustomizations-example.webxml
-qtcharts-qmlcustomlegend-example.webxml
-qtcharts-qmlf1legends-example.webxml
-qtcharts-qmloscilloscope-example.webxml
-qtcharts-qmlweather-example.webxml
-qtcharts-scatterchart-example.webxml
-qtcharts-selectedbar-example.webxml
-qtcharts-splinechart-example.webxml
-qtcharts-stackedbarchart-example.webxml
-qtcharts-zoomlinechart-example.webxml
-qtconcurrent-imagescaling-example.webxml
-qtconcurrent-wordcount-example.webxml
-qtconcurrentfilter.webxml
-qtconcurrentmap.webxml
-qtconcurrentrun.webxml
-qtconcurrenttask.webxml
-qtcore-changes-qt6.webxml
-qtcore-ipc-localfortuneclient-example.webxml
-qtcore-ipc-localfortuneserver-example.webxml
-qtcore-platform-androidnotifier-example.webxml
-qtcore-serialization-savegame-example.webxml
-qtcore-threads-queuedcustomtype-example.webxml
-qtcore-threads-semaphores-example.webxml
-qtcore-threads-waitconditions-example.webxml
-qtcore-tools-contiguouscache-example.webxml
-qtdbus-chat-example.webxml
-qtdbus-cmake-qt-add-dbus-adaptor.webxml
-qtdbus-cmake-qt-add-dbus-interface.webxml
-qtdbus-cmake-qt-add-dbus-interfaces.webxml
-qtdbus-cmake-qt-generate-dbus-interface.webxml
-qtdbus-complexpingpong-example.webxml
-qtdbus-remotecontrolledcar-example.webxml
-qtdesigner-calculatorbuilder-example.webxml
-qtdesigner-calculatorform-example.webxml
-qtdesigner-components.webxml
-qtdesigner-containerextension-example.webxml
-qtdesigner-customwidgetplugin-example.webxml
-qtdesigner-manual.webxml
-qtest-overview.webxml
-qtest-tutorial.webxml
-qtextedit-extraselection.webxml
-qtextlayout-formatrange.webxml
-qtextoption-tab.webxml
-qtglobal.webxml
-qtgui-rasterwindow-example.webxml
-qthelp-contextsensitivehelp-example.webxml
-qthelp-framework.webxml
-qthelpproject.webxml
-qtilerules.webxml
-qtimezone-offsetdata.webxml
-qtjavascript.webxml
-qtmath.webxml
-qtmultimedia-apple.webxml
-qtmultimedia-changes-qt6.webxml
-qtnetwork-broadcastreceiver-example.webxml
-qtnetwork-broadcastsender-example.webxml
-qtnetwork-http-example.webxml
-qtnetwork-multicastreceiver-example.webxml
-qtnetwork-multicastsender-example.webxml
-qtnetwork-network-chat-example.webxml
-qtnetwork-programming.webxml
-qtnetwork-securesocketclient-example.webxml
-qtnetwork-secureudpclient-example.webxml
-qtnetwork-secureudpserver-example.webxml
-qtnetwork-torrent-example.webxml
-qtnfc-annotatedurl-example.webxml
-qtnfc-changes-qt6.webxml
-qtnfc-ndefeditor-example.webxml
-qtnfc-overview.webxml
-qtopengl-2dpainting-example.webxml
-qtopengl-cube-example.webxml
-qtopengl-hellogles3-example.webxml
-qtopengl-openglwindow-example.webxml
-qtplugin.webxml
-qtpositioning-android.webxml
-qtpositioning-changes-qt6.webxml
-qtpositioning-examples.webxml
-qtpositioning-ios.webxml
-qtpositioning-logfilepositionsource-example.webxml
-qtpositioning-plugins.webxml
-qtpositioning-satelliteinfo-example.webxml
-qtpositioning-weatherinfo-example.webxml
-qtqml-documents-definetypes.webxml
-qtqml-documents-networktransparency.webxml
-qtqml-documents-scope.webxml
-qtqml-documents-structure.webxml
-qtqml-documents-topic.webxml
-qtqml-dynamicscene-example.webxml
-qtqml-javascript-dynamicobjectcreation.webxml
-qtqml-javascript-expressions.webxml
-qtqml-javascript-finetuning.webxml
-qtqml-javascript-functionlist.webxml
-qtqml-javascript-hostenvironment.webxml
-qtqml-javascript-imports.webxml
-qtqml-javascript-qmlglobalobject.webxml
-qtqml-javascript-resources.webxml
-qtqml-javascript-topic.webxml
-qtqml-qml-i18n-example.webxml
-qtqml-syntax-basics.webxml
-qtqml-syntax-directoryimports.webxml
-qtqml-syntax-imports.webxml
-qtqml-syntax-objectattributes.webxml
-qtqml-syntax-propertybinding.webxml
-qtqml-tutorials-extending-qml-example.webxml
-qtqml-typesystem-basictypes.webxml
-qtqml-typesystem-objecttypes.webxml
-qtqml-typesystem-topic.webxml
-qtquick-animation-example.webxml
-qtquick-bestpractices.webxml
-qtquick-canvas-example.webxml
-qtquick-codesamples.webxml
-qtquick-convenience-topic.webxml
-qtquick-cppextensionpoints.webxml
-qtquick-customitems-dialcontrol-example.webxml
-qtquick-customitems-flipable-example.webxml
-qtquick-customitems-painteditem-example.webxml
-qtquick-draganddrop-example.webxml
-qtquick-effects-particles.webxml
-qtquick-effects-sprites.webxml
-qtquick-effects-topic.webxml
-qtquick-effects-transformations.webxml
-qtquick-imageelements-example.webxml
-qtquick-imageprovider-example.webxml
-qtquick-imageresponseprovider-example.webxml
-qtquick-input-focus.webxml
-qtquick-input-mouseevents.webxml
-qtquick-input-textinput.webxml
-qtquick-input-topic.webxml
-qtquick-keyinteraction-example.webxml
-qtquick-layouts-example.webxml
-qtquick-localstorage-example.webxml
-qtquick-models-abstractitemmodel-example.webxml
-qtquick-modelviewsdata-cppmodels.webxml
-qtquick-modelviewsdata-modelview.webxml
-qtquick-modelviewsdata-topic.webxml
-qtquick-mousearea-example.webxml
-qtquick-particles-affectors-example.webxml
-qtquick-particles-emitters-example.webxml
-qtquick-particles-imageparticle-example.webxml
-qtquick-particles-performance.webxml
-qtquick-particles-system-example.webxml
-qtquick-positioners-example.webxml
-qtquick-positioning-anchors.webxml
-qtquick-positioning-layouts.webxml
-qtquick-positioning-righttoleft.webxml
-qtquick-positioning-topic.webxml
-qtquick-quick-accessibility-example.webxml
-qtquick-quickwidgets-quickwidget-example.webxml
-qtquick-scenegraph-custommaterial-example.webxml
-qtquick-scenegraph-d3d11underqml-example.webxml
-qtquick-scenegraph-graph-example.webxml
-qtquick-scenegraph-materials.webxml
-qtquick-scenegraph-metaltextureimport-example.webxml
-qtquick-scenegraph-metalunderqml-example.webxml
-qtquick-scenegraph-nodes.webxml
-qtquick-scenegraph-twotextureproviders-example.webxml
-qtquick-scenegraph-vulkantextureimport-example.webxml
-qtquick-scenegraph-vulkanunderqml-example.webxml
-qtquick-shadereffects-example.webxml
-qtquick-shapes-example.webxml
-qtquick-statesanimations-animations.webxml
-qtquick-statesanimations-behaviors.webxml
-qtquick-statesanimations-states.webxml
-qtquick-statesanimations-topic.webxml
-qtquick-tableview-gameoflife-example.webxml
-qtquick-tableview-pixelator-example.webxml
-qtquick-text-example.webxml
-qtquick-tool-qmllint.webxml
-qtquick-tools-and-utilities.webxml
-qtquick-tutorials-dynamicview-dynamicview1-example.webxml
-qtquick-tutorials-dynamicview-dynamicview2-example.webxml
-qtquick-tutorials-dynamicview-dynamicview3-example.webxml
-qtquick-tutorials-dynamicview-dynamicview4-example.webxml
-qtquick-tutorials-samegame-samegame1-example.webxml
-qtquick-tutorials-samegame-samegame2-example.webxml
-qtquick-tutorials-samegame-samegame3-example.webxml
-qtquick-tutorials-samegame-samegame4-example.webxml
-qtquick-views-example.webxml
-qtquick-visualcanvas-adaptations-openvg.webxml
-qtquick-visualcanvas-adaptations-software.webxml
-qtquick-visualcanvas-adaptations.webxml
-qtquick-visualcanvas-coordinates.webxml
-qtquick-visualcanvas-scenegraph-renderer.webxml
-qtquick-visualcanvas-scenegraph.webxml
-qtquick-visualcanvas-topic.webxml
-qtquick-visualcanvas-visualparent.webxml
-qtquick-visualtypes-topic.webxml
-qtquickcontrols-changes-qt6.webxml
-qtquickcontrols-chattutorial-example.webxml
-qtquickcontrols-contactlist-example.webxml
-qtquickcontrols-eventcalendar-example.webxml
-qtquickcontrols-flatstyle-example.webxml
-qtquickcontrols-imagine-automotive-example.webxml
-qtquickcontrols-texteditor-example.webxml
-qtquickcontrols-wearable-example.webxml
-qtquickcontrols-basic.webxml
-qtquickcontrols-buttons.webxml
-qtquickcontrols-configuration.webxml
-qtquickcontrols-containers.webxml
-qtquickcontrols-customize.webxml
-qtquickcontrols-delegates.webxml
-qtquickcontrols-deployment.webxml
-qtquickcontrols-environment.webxml
-qtquickcontrols-examples.webxml
-qtquickcontrols-fileselectors.webxml
-qtquickcontrols-focus.webxml
-qtquickcontrols-fusion.webxml
-qtquickcontrols-gettingstarted.webxml
-qtquickcontrols-guidelines.webxml
-qtquickcontrols-icons.webxml
-qtquickcontrols-imagine.webxml
-qtquickcontrols-indicators.webxml
-qtquickcontrols-input.webxml
-qtquickcontrols-macos.webxml
-qtquickcontrols-material.webxml
-qtquickcontrols-menus.webxml
-qtquickcontrols-navigation.webxml
-qtquickcontrols-popups.webxml
-qtquickcontrols-separators.webxml
-qtquickcontrols-styles.webxml
-qtquickcontrols-universal.webxml
-qtquickcontrols-windows.webxml
-qtquicklayouts-overview.webxml
-qtremoteobjects-cmake-qt-add-repc-merged.webxml
-qtremoteobjects-cmake-qt-add-repc-replicas.webxml
-qtremoteobjects-cmake-qt-add-repc-sources.webxml
-qtremoteobjects-cmake-qt-rep-from-headers.webxml
-qtremoteobjects-compatibility.webxml
-qtremoteobjects-custom-transport.webxml
-qtremoteobjects-external-schemas.webxml
-qtremoteobjects-gettingstarted.webxml
-qtremoteobjects-interaction.webxml
-qtremoteobjects-node.webxml
-qtremoteobjects-registry.webxml
-qtremoteobjects-repc.webxml
-qtremoteobjects-replica.webxml
-qtremoteobjects-source.webxml
-qtremoteobjects-ssl-example.webxml
-qtremoteobjects-troubleshooting.webxml
-qtremoteobjects-websockets-example.webxml
-qtscxml-changes-qt6.webxml
-qtscxml-cmake-qt-add-statecharts.webxml
-qtscxml-ftpclient-example.webxml
-qtscxml-instantiating-state-machines.webxml
-qtscxml-overview.webxml
-qtscxml-scxml-compliance.webxml
-qtscxml-sudoku-example.webxml
-qtscxml-trafficlight-qml-dynamic-example.webxml
-qtscxml-trafficlight-qml-simple-example.webxml
-qtscxml-trafficlight-qml-static-example.webxml
-qtscxml-trafficlight-widgets-dynamic-example.webxml
-qtscxml-trafficlight-widgets-static-example.webxml
-qtsensors-changes-qt6.webxml
-qtsensors-cpp.webxml
-qtsensors-examples.webxml
-qtsensors-sensorsshowcase-example.webxml
-qtserialport-blockingreceiver-example.webxml
-qtserialport-blockingsender-example.webxml
-qtserialport-changes-qt6.webxml
-qtserialport-examples.webxml
-qtserialport-terminal-example.webxml
-qtsql-cachedtable-example.webxml
-qtsql-changes-qt6.webxml
-qtsql-drilldown-example.webxml
-qtsql-masterdetail-example.webxml
-qtsql-querymodel-example.webxml
-qtsql-sqlbrowser-example.webxml
-qtsql-sqlwidgetmapper-example.webxml
-qtsql-tablemodel-example.webxml
-qtsvg-changes-qt6.webxml
-qttest-best-practices-qdoc.webxml
-qttestlib-tutorial1-example.webxml
-qttestlib-tutorial2-example.webxml
-qttestlib-tutorial3-example.webxml
-qttestlib-tutorial4-example.webxml
-qttestlib-tutorial5-example.webxml
-qttestlib-tutorial6.webxml
-qtuitools-textfinder-example.webxml
-qtwebchannel-changes-qt6.webxml
-qtwebchannel-chatclient-html-example.webxml
-qtwebchannel-chatclient-qml-example.webxml
-qtwebchannel-chatserver-cpp-example.webxml
-qtwebchannel-examples.webxml
-qtwebchannel-javascript.webxml
-qtwebengine-changes-qt6.webxml
-qtwebengine-features.webxml
-qtwebengine-overview.webxml
-qtwebengine-platform-notes.webxml
-qtwebengine-webenginequick-lifecycle-example.webxml
-qtwebengine-webenginewidgets-contentmanipulation-example.webxml
-qtwebengine-webenginewidgets-cookiebrowser-example.webxml
-qtwebengine-webenginewidgets-html2pdf-example.webxml
-qtwebengine-webenginewidgets-maps-example.webxml
-qtwebengine-webenginewidgets-printme-example.webxml
-qtwebengine-webenginewidgets-spellchecker-example.webxml
-qtwebengine-webenginewidgets-videoplayer-example.webxml
-qtwebenginewidgets-qtwebkitportingguide.webxml
-qtwebsockets-changes-qt6.webxml
-qtwebsockets-echoclient-example.webxml
-qtwebsockets-echoserver-example.webxml
-qtwebsockets-examples.webxml
-qtwebsockets-qmlwebsocketclient-example.webxml
-qtwebsockets-qmlwebsocketserver-example.webxml
-qtwebsockets-simplechat-example.webxml
-qtwebsockets-sslechoclient-example.webxml
-qtwebsockets-sslechoserver-example.webxml
-qtwebsockets-testing.webxml
-qtwidgets-gestures-imagegestures-example.webxml
-qtwidgets-graphicsview-basicgraphicslayouts-example.webxml
-qtwidgets-graphicsview-chip-example.webxml
-qtwidgets-graphicsview-simpleanchorlayout-example.webxml
-qtwidgets-itemviews-coloreditorfactory-example.webxml
-qtwidgets-itemviews-combowidgetmapper-example.webxml
-qtwidgets-itemviews-customsortfiltermodel-example.webxml
-qtwidgets-itemviews-frozencolumn-example.webxml
-qtwidgets-itemviews-simpletreemodel-example.webxml
-qtwidgets-mainwindows-menus-example.webxml
-qtwidgets-painting-affine-example.webxml
-qtwidgets-painting-composition-example.webxml
-qtwidgets-painting-deform-example.webxml
-qtwidgets-painting-gradients-example.webxml
-qtwidgets-painting-imagecomposition-example.webxml
-qtwidgets-painting-painterpaths-example.webxml
-qtwidgets-painting-pathstroke-example.webxml
-qtwidgets-painting-transformations-example.webxml
-qtwidgets-tools-completer-example.webxml
-qtwidgets-tools-customcompleter-example.webxml
-qtwidgets-tools-echoplugin-example.webxml
-qtwidgets-tools-settingseditor-example.webxml
-qtwidgets-tools-styleplugin-example.webxml
-qtwidgets-tools-treemodelcompleter-example.webxml
-qtwidgets-tools-undoframework-example.webxml
-qtwidgets-touch-knobs-example.webxml
-qtwidgets-tutorials-notepad-example.webxml
-qtwidgets-tutorials-widgets-childwidget-example.webxml
-qtwidgets-tutorials-widgets-nestedlayouts-example.webxml
-qtwidgets-tutorials-widgets-toplevel-example.webxml
-qtwidgets-tutorials-widgets-windowlayout-example.webxml
-qtwidgets-widgets-analogclock-example.webxml
-qtwidgets-widgets-calculator-example.webxml
-qtwidgets-widgets-calendarwidget-example.webxml
-qtwidgets-widgets-groupbox-example.webxml
-qtwidgets-widgets-lineedits-example.webxml
-qtwidgets-widgets-scribble-example.webxml
-qtwidgets-widgets-shapedclock-example.webxml
-qtwidgets-widgets-sliders-example.webxml
-qtwidgets-widgets-spinboxes-example.webxml
-qtwidgets-widgets-tablet-example.webxml
-qtwidgets-widgets-windowflags-example.webxml
-quick-changes-qt6.webxml
-qwebenginecookiestore-filterrequest.webxml
-qwidget-styling.webxml
-remoteobjects-changes-qt6.webxml
-remoteobjects-example-dynamic-replica.webxml
-remoteobjects-example-registry.webxml
-remoteobjects-example-static-source.webxml
-resources.webxml
-richtext-advanced-processing.webxml
-richtext-common-tasks.webxml
-richtext-cursor.webxml
-richtext-html-subset.webxml
-richtext-layouts.webxml
-richtext-processing.webxml
-richtext-structure.webxml
-richtext.webxml
-senorfwbackend.webxml
-sensors-backend-topics.webxml
-shared.webxml
-signalsandslots.webxml
-sql-connecting.webxml
-sql-driver.webxml
-sql-forms.webxml
-sql-model.webxml
-sql-presenting.webxml
-sql-programming.webxml
-sql-sqlstatements.webxml
-sql-types.webxml
-ssl.webxml
-standard-dialogs.webxml
-style-reference.webxml
-stylesheet-customizing.webxml
-stylesheet-designer.webxml
-stylesheet-examples.webxml
-stylesheet-reference.webxml
-stylesheet-syntax.webxml
-stylesheet.webxml
-svgrendering.webxml
-testlib-changes-qt6.webxml
-timers.webxml
-usingadaptors.webxml
-videooverview.webxml
-webengine-examples.webxml
-webengine-widgetexamples.webxml
-websockets-overview.webxml
-widget-classes.webxml
-widgets-changes-qt6.webxml
-widgets-tutorial.webxml
-xml-changes-qt6.webxml
-xml-dom-tml.webxml
-xml-namespaces.webxml
-xml-processing.webxml
-xml-streaming.webxml
-xml-tools.webxml
-# qtdoc repository
-accessible.webxml
-appicon.webxml
-create-your-first-applications.webxml
-deployment.webxml
-desktop-integration.webxml
-exceptionsafety.webxml
-explore-qt.webxml
-get-and-install-qt.webxml
-gettingstarted.webxml
-highdpi.webxml
-install-qt-design-studio.webxml
-ipc.webxml
-known-issues.webxml
-overviews-main.webxml
-plugins-howto.webxml
-qml-codingconventions.webxml
-qml-glossary.webxml
-qmlapplications.webxml
-qmlfirststeps.webxml
-qt-intro.webxml
-qtquick-debugging.webxml
-qtquick-performance.webxml
-qtquick-qml-runtime.webxml
-qtquick-usecase-animations.webxml
-qtquick-usecase-integratingjs.webxml
-qtquick-usecase-layouts.webxml
-qtquick-usecase-styling.webxml
-qtquick-usecase-text.webxml
-qtquick-usecase-userinput.webxml
-qtquick-usecase-visual.webxml
-qundo.webxml
-rcc.webxml
-restoring-geometry.webxml
-scalability.webxml
-session.webxml
-sharedlibrary.webxml
-solutions-for-application-development.webxml
-solutions-for-ui-design.webxml
-thread-basics.webxml
-threads.webxml
-tools-for-qt-quick-uis.webxml
-tools-for-qt-widget-based-uis.webxml
-topics-app-development.webxml
-topics-data-io.webxml
-topics-ui.webxml
-uic.webxml
-unicode.webxml
-wayland-and-qt.webxml
+qt3d/webxml/qt3d-changes-qt6.webxml
+qt3d/webxml/qt3d-cpp.webxml
+qt3d/webxml/qt3d-overview.webxml
+qt3d/webxml/qt3d-qml.webxml
+qt3d/webxml/qt3drender-framegraph.webxml
+qt3d/webxml/qt3drender-geometry.webxml
+qt3d/webxml/qt3drender-porting-to-rhi.webxml
+qt3d/webxml/qt3drender-protips.webxml
+qtbluetooth/webxml/qtbluetooth-changes-qt6.webxml
+qtbluetooth/webxml/qtbluetooth-le-overview.webxml
+qtbluetooth/webxml/qtbluetooth-overview.webxml
+qtcharts/webxml/qtcharts-changes-qt6.webxml
+qtcharts/webxml/qtcharts-overview.webxml
+qtconcurrent/webxml/concurrent-changes-qt6.webxml
+qtconcurrent/webxml/qtconcurrentfilter.webxml
+qtconcurrent/webxml/qtconcurrentmap.webxml
+qtconcurrent/webxml/qtconcurrentrun.webxml
+qtconcurrent/webxml/qtconcurrenttask.webxml
+qtcore/webxml/animation-overview.webxml
+qtcore/webxml/bindableproperties.webxml
+qtcore/webxml/cbor.webxml
+qtcore/webxml/containers.webxml
+qtcore/webxml/custom-types.webxml
+qtcore/webxml/datastreamformat.webxml
+qtcore/webxml/eventsandfilters.webxml
+qtcore/webxml/implicit-sharing.webxml
+qtcore/webxml/io-functions.webxml
+qtcore/webxml/ipc.webxml
+qtcore/webxml/java-style-iterators.webxml
+qtcore/webxml/json.webxml
+qtcore/webxml/metaobjects.webxml
+qtcore/webxml/objecttrees.webxml
+qtcore/webxml/object.webxml
+qtcore/webxml/permissions.webxml
+qtcore/webxml/plugins.webxml
+qtcore/webxml/properties.webxml
+qtcore/webxml/qabstracteventdispatcher-timerinfo.webxml
+qtcore/webxml/qadoptshareddatatag.webxml
+qtcore/webxml/qcborerror.webxml
+qtcore/webxml/qcborparsererror.webxml
+qtcore/webxml/qcborstreamreader-stringresult.webxml
+qtcore/webxml/qglobalstatic.webxml
+qtcore/webxml/qhashseed.webxml
+qtcore/webxml/qiterator.webxml
+qtcore/webxml/qjsonparseerror.webxml
+qtcore/webxml/qprocess-createprocessarguments.webxml
+qtcore/webxml/qstaticplugin.webxml
+qtcore/webxml/qtalgorithms.webxml
+qtcore/webxml/qtcborcommon.webxml
+qtcore/webxml/qtcore-changes-qt6.webxml
+qtcore/webxml/qtglobal.webxml
+qtcore/webxml/qtimezone-offsetdata.webxml
+qtcore/webxml/qtmath.webxml
+qtcore/webxml/qtplugin.webxml
+qtcore/webxml/resources.webxml
+qtcore/webxml/shared.webxml
+qtcore/webxml/signalsandslots.webxml
+qtcore/webxml/timers.webxml
+qtdbus/webxml/dbus-changes-qt6.webxml
+qtdbus/webxml/qdbusdeclaringsignals.webxml
+qtdbus/webxml/qdbusdeclaringslots.webxml
+qtdbus/webxml/qdbustypesystem.webxml
+qtdbus/webxml/qdbusviewer.webxml
+qtdbus/webxml/qdbusxml2cpp.webxml
+qtdbus/webxml/qtdbus-cmake-qt-add-dbus-adaptor.webxml
+qtdbus/webxml/qtdbus-cmake-qt-add-dbus-interfaces.webxml
+qtdbus/webxml/qtdbus-cmake-qt-add-dbus-interface.webxml
+qtdbus/webxml/qtdbus-cmake-qt-generate-dbus-interface.webxml
+qtdbus/webxml/usingadaptors.webxml
+qtdesigner/webxml/qtdesigner-components.webxml
+qtdesigner/webxml/qtdesigner-manual.webxml
+qtdoc/webxml/accessible.webxml
+qtdoc/webxml/appicon.webxml
+qtdoc/webxml/create-your-first-applications.webxml
+qtdoc/webxml/deployment.webxml
+qtdoc/webxml/desktop-integration.webxml
+qtdoc/webxml/exceptionsafety.webxml
+qtdoc/webxml/explore-qt.webxml
+qtdoc/webxml/get-and-install-qt.webxml
+qtdoc/webxml/gettingstarted.webxml
+qtdoc/webxml/highdpi.webxml
+qtdoc/webxml/install-qt-design-studio.webxml
+qtdoc/webxml/known-issues.webxml
+qtdoc/webxml/mobiledevelopment.webxml
+qtdoc/webxml/overviews-main.webxml
+qtdoc/webxml/plugins-howto.webxml
+qtdoc/webxml/qmlapplications.webxml
+qtdoc/webxml/qml-codingconventions.webxml
+qtdoc/webxml/qmlfirststeps.webxml
+qtdoc/webxml/qml-glossary.webxml
+qtdoc/webxml/qt-intro.webxml
+qtdoc/webxml/qtlanguages.webxml
+qtdoc/webxml/qtquick-debugging.webxml
+qtdoc/webxml/qtquick-performance.webxml
+qtdoc/webxml/qtquick-qml-runtime.webxml
+qtdoc/webxml/qtquick-usecase-animations.webxml
+qtdoc/webxml/qtquick-usecase-integratingjs.webxml
+qtdoc/webxml/qtquick-usecase-layouts.webxml
+qtdoc/webxml/qtquick-usecase-styling.webxml
+qtdoc/webxml/qtquick-usecase-text.webxml
+qtdoc/webxml/qtquick-usecase-userinput.webxml
+qtdoc/webxml/qtquick-usecase-visual.webxml
+qtdoc/webxml/qt-releases.webxml
+qtdoc/webxml/qundo.webxml
+qtdoc/webxml/rcc.webxml
+qtdoc/webxml/restoring-geometry.webxml
+qtdoc/webxml/scalability.webxml
+qtdoc/webxml/security.webxml
+qtdoc/webxml/session.webxml
+qtdoc/webxml/sharedlibrary.webxml
+qtdoc/webxml/solutions-for-application-development.webxml
+qtdoc/webxml/solutions-for-ui-design.webxml
+qtdoc/webxml/testing-and-debugging.webxml
+qtdoc/webxml/thread-basics.webxml
+qtdoc/webxml/threads.webxml
+qtdoc/webxml/tools-for-qt-quick-uis.webxml
+qtdoc/webxml/tools-for-qt-widget-based-uis.webxml
+qtdoc/webxml/topics-app-development.webxml
+qtdoc/webxml/topics-core.webxml
+qtdoc/webxml/topics-data-io.webxml
+qtdoc/webxml/topics-graphics.webxml
+qtdoc/webxml/topics-network-connectivity.webxml
+qtdoc/webxml/topics-ui.webxml
+qtdoc/webxml/uic.webxml
+qtdoc/webxml/unicode.webxml
+qtdoc/webxml/untrusteddata.webxml
+qtdoc/webxml/wayland-and-qt.webxml
+qtgraphs/webxml/qtgraphs-and-qtquick3d-integration-guide.webxml
+qtgraphs/webxml/qtgraphs-configure-options.webxml
+qtgraphs/webxml/qtgraphs-data-handling.webxml
+qtgraphs/webxml/qtgraphs-interacting-with-data.webxml
+qtgraphs/webxml/qtgraphs-known-issues.webxml
+qtgraphs/webxml/qtgraphs-migration-guide-2d.webxml
+qtgraphs/webxml/qtgraphs-migration-guide.webxml
+qtgraphs/webxml/qtgraphs-overview-2d.webxml
+qtgraphs/webxml/qtgraphs-overview-3d.webxml
+qtgraphs/webxml/qtgraphs-overview-theme.webxml
+qtgui/webxml/coordsys.webxml
+qtgui/webxml/dnd.webxml
+qtgui/webxml/gui-changes-qt6.webxml
+qtgui/webxml/paintsystem-devices.webxml
+qtgui/webxml/paintsystem-drawing.webxml
+qtgui/webxml/paintsystem-images.webxml
+qtgui/webxml/paintsystem.webxml
+qtgui/webxml/qabstracttextdocumentlayout-paintcontext.webxml
+qtgui/webxml/qabstracttextdocumentlayout-selection.webxml
+qtgui/webxml/qaccessible-state.webxml
+qtgui/webxml/qiconengine-scaledpixmapargument.webxml
+qtgui/webxml/qpageranges-range.webxml
+qtgui/webxml/qtextlayout-formatrange.webxml
+qtgui/webxml/qtextoption-tab.webxml
+qtgui/webxml/qtgui-overview.webxml
+qtgui/webxml/richtext-advanced-processing.webxml
+qtgui/webxml/richtext-common-tasks.webxml
+qtgui/webxml/richtext-cursor.webxml
+qtgui/webxml/richtext-html-subset.webxml
+qtgui/webxml/richtext-layouts.webxml
+qtgui/webxml/richtext-structure.webxml
+qtgui/webxml/richtext.webxml
+qthelp/webxml/helpsystem.webxml
+qthelp/webxml/qhelplink.webxml
+qthelp/webxml/qthelp-framework.webxml
+qthelp/webxml/qthelpproject.webxml
+qthttpserver/webxml/qthttpserver-colorpalette-apibehavior-h.webxml
+qthttpserver/webxml/qthttpserver-colorpalette-colorpalette-pro.webxml
+qthttpserver/webxml/qthttpserver-colorpalette-main-cpp.webxml
+qthttpserver/webxml/qthttpserver-colorpalette-types-h.webxml
+qthttpserver/webxml/qthttpserver-colorpalette-utils-h.webxml
+qthttpserver/webxml/qthttpserver-logging.webxml
+qtlocation/webxml/location-places-backend.webxml
+qtlocation/webxml/location-plugin-itemsoverlay.webxml
+qtlocation/webxml/location-plugin-osm.webxml
+qtlocation/webxml/qml-location5-maps.webxml
+qtlocation/webxml/qtlocation-geoservices.webxml
+qtmultimedia/webxml/advanced-ffmpeg-configuration.webxml
+qtmultimedia/webxml/audiooverview.webxml
+qtmultimedia/webxml/cameraoverview.webxml
+qtmultimedia/webxml/multimediaoverview.webxml
+qtmultimedia/webxml/qmediatimerange-interval.webxml
+qtmultimedia/webxml/qt-add-ios-ffmpeg-libraries.webxml
+qtmultimedia/webxml/qtmultimedia-apple.webxml
+qtmultimedia/webxml/qtmultimedia-changes-qt6.webxml
+qtmultimedia/webxml/qtmultimedia-gstreamer.webxml
+qtmultimedia/webxml/qtmultimedia-modules.webxml
+qtmultimedia/webxml/qtmultimedia-wayland.webxml
+qtmultimedia/webxml/qtmultimedia-windows.webxml
+qtmultimedia/webxml/videooverview.webxml
+qtnetworkauth/webxml/oauth-http-method-alternatives.webxml
+qtnetwork/webxml/network-changes-qt6.webxml
+qtnetwork/webxml/qdtlsclientverifier-generatorparameters.webxml
+qtnetwork/webxml/qtnetwork-programming.webxml
+qtnetwork/webxml/ssl.webxml
+qtnfc/webxml/nfc-android.webxml
+qtnfc/webxml/qndeffilter-record.webxml
+qtnfc/webxml/qtnfc-changes-qt6.webxml
+qtnfc/webxml/qtnfc-features.webxml
+qtnfc/webxml/qtnfc-overview.webxml
+qtnfc/webxml/qtnfc-pcsc.webxml
+qtopengl/webxml/opengl-changes-qt6.webxml
+qtpositioning/webxml/positioning-cpp-qml.webxml
+qtpositioning/webxml/position-plugin-android.webxml
+qtpositioning/webxml/position-plugin-geoclue2.webxml
+qtpositioning/webxml/position-plugin-gypsy.webxml
+qtpositioning/webxml/position-plugin-nmea.webxml
+qtpositioning/webxml/qtpositioning-android.webxml
+qtpositioning/webxml/qtpositioning-changes-qt6.webxml
+qtpositioning/webxml/qtpositioning-ios.webxml
+qtpositioning/webxml/qtpositioning-plugins.webxml
+qtprintsupport/webxml/pdf-licensing.webxml
+qtprintsupport/webxml/printsupport-changes-qt6.webxml
+qtqml/webxml/qml-changes-qt6.webxml
+qtqml/webxml/qmldiskcache.webxml
+qtqml/webxml/qmlreference.webxml
+qtqml/webxml/qml-singleton.webxml
+qtqml/webxml/qqmlcontext-propertypair.webxml
+qtqml/webxml/qt-add-qml-plugin.webxml
+qtqml/webxml/qt-import-qml-plugins.webxml
+qtqml/webxml/qtjavascript.webxml
+qtqml/webxml/qtqml-documents-definetypes.webxml
+qtqml/webxml/qtqml-documents-networktransparency.webxml
+qtqml/webxml/qtqml-documents-scope.webxml
+qtqml/webxml/qtqml-documents-structure.webxml
+qtqml/webxml/qtqml-documents-topic.webxml
+qtqml/webxml/qtqml-javascript-dynamicobjectcreation.webxml
+qtqml/webxml/qtqml-javascript-expressions.webxml
+qtqml/webxml/qtqml-javascript-finetuning.webxml
+qtqml/webxml/qtqml-javascript-functionlist.webxml
+qtqml/webxml/qtqml-javascript-hostenvironment.webxml
+qtqml/webxml/qtqml-javascript-imports.webxml
+qtqml/webxml/qtqml-javascript-memory.webxml
+qtqml/webxml/qtqml-javascript-qmlglobalobject.webxml
+qtqml/webxml/qtqml-javascript-resources.webxml
+qtqml/webxml/qtqml-javascript-topic.webxml
+qtqml/webxml/qtqml-modules-identifiedmodules.webxml
+qtqml/webxml/qtqml-modules-legacymodules.webxml
+qtqml/webxml/qtqml-modules-qmldir.webxml
+qtqml/webxml/qtqml-modules-topic.webxml
+qtqml/webxml/qtqml-syntax-basics.webxml
+qtqml/webxml/qtqml-syntax-directoryimports.webxml
+qtqml/webxml/qtqml-syntax-imports.webxml
+qtqml/webxml/qtqml-syntax-objectattributes.webxml
+qtqml/webxml/qtqml-syntax-propertybinding.webxml
+qtqml/webxml/qtqml-syntax-signals.webxml
+qtqml/webxml/qtqml-typesystem-basictypes.webxml
+qtqml/webxml/qtqml-typesystem-namespaces.webxml
+qtqml/webxml/qtqml-typesystem-objecttypes.webxml
+qtqml/webxml/qtqml-typesystem-references.webxml
+qtqml/webxml/qtqml-typesystem-sequencetypes.webxml
+qtqml/webxml/qtqml-typesystem-topic.webxml
+qtqml/webxml/qtqml-typesystem-valuetypes.webxml
+qtqml/webxml/qt-target-qml-sources.webxml
+qtquick3d/webxml/qt3dxr-multiview.webxml
+qtquick3d/webxml/qt3dxr-pcvr.webxml
+qtquick3d/webxml/qt3dxr-quest-quick-start.webxml
+qtquick3d/webxml/qt3dxr-quick-start-guide-applevisionpro.webxml
+qtquick3d/webxml/qt3dxr-supported-openxr-extensions.webxml
+qtquick3d/webxml/qt3dxr-supported-platforms.webxml
+qtquick3d/webxml/qtaa-toc.webxml
+qtquick3d/webxml/qtquick3d-2d.webxml
+qtquick3d/webxml/qtquick3d-architecture.webxml
+qtquick3d/webxml/qtquick3d-custom.webxml
+qtquick3d/webxml/qtquick3d-lod.webxml
+qtquick3d/webxml/qtquick3d-requirements.webxml
+qtquick3d/webxml/qt-quick-3d-xr.webxml
+qtquick3d/webxml/qtxr-locomotion.webxml
+qtquick3d/webxml/quick3d-asset-conditioning-3d-assets.webxml
+qtquick3d/webxml/quick3d-asset-conditioning-anti-aliasing.webxml
+qtquick3d/webxml/quick3d-asset-conditioning-export-blender.webxml
+qtquick3d/webxml/quick3d-asset-conditioning-export-max.webxml
+qtquick3d/webxml/quick3d-asset-conditioning-export-maya.webxml
+qtquick3d/webxml/quick3d-asset-conditioning-export-modo.webxml
+qtquick3d/webxml/quick3d-asset-conditioning-ibl.webxml
+qtquick3d/webxml/quick3d-asset-intro.webxml
+qtquick3d/webxml/quick3d-instancing.webxml
+qtquick3d/webxml/quick3d-lightmap.webxml
+qtquick3d/webxml/quick3d-morphing.webxml
+qtquick3d/webxml/quick3d-pbr.webxml
+qtquick3d/webxml/quick3d-shadow-mapping.webxml
+qtquick3d/webxml/quick3d-vertex-skinning.webxml
+qtquickcontrols/webxml/qtquickcontrols-basic.webxml
+qtquickcontrols/webxml/qtquickcontrols-buttons.webxml
+qtquickcontrols/webxml/qtquickcontrols-changes-qt6.webxml
+qtquickcontrols/webxml/qtquickcontrols-configuration.webxml
+qtquickcontrols/webxml/qtquickcontrols-containers.webxml
+qtquickcontrols/webxml/qtquickcontrols-customize.webxml
+qtquickcontrols/webxml/qtquickcontrols-delegates.webxml
+qtquickcontrols/webxml/qtquickcontrols-deployment.webxml
+qtquickcontrols/webxml/qtquickcontrols-environment.webxml
+qtquickcontrols/webxml/qtquickcontrols-fileselectors.webxml
+qtquickcontrols/webxml/qtquickcontrols-fluentwinui3.webxml
+qtquickcontrols/webxml/qtquickcontrols-focus.webxml
+qtquickcontrols/webxml/qtquickcontrols-fusion.webxml
+qtquickcontrols/webxml/qtquickcontrols-gettingstarted.webxml
+qtquickcontrols/webxml/qtquickcontrols-guidelines.webxml
+qtquickcontrols/webxml/qtquickcontrols-icons.webxml
+qtquickcontrols/webxml/qtquickcontrols-imagine.webxml
+qtquickcontrols/webxml/qtquickcontrols-indicators.webxml
+qtquickcontrols/webxml/qtquickcontrols-input.webxml
+qtquickcontrols/webxml/qtquickcontrols-ios.webxml
+qtquickcontrols/webxml/qtquickcontrols-macos.webxml
+qtquickcontrols/webxml/qtquickcontrols-material.webxml
+qtquickcontrols/webxml/qtquickcontrols-menus.webxml
+qtquickcontrols/webxml/qtquickcontrols-navigation.webxml
+qtquickcontrols/webxml/qtquickcontrols-popups.webxml
+qtquickcontrols/webxml/qtquickcontrols-separators.webxml
+qtquickcontrols/webxml/qtquickcontrols-styles.webxml
+qtquickcontrols/webxml/qtquickcontrols-universal.webxml
+qtquickcontrols/webxml/qtquickcontrols-windows.webxml
+qtquick/webxml/qml-advtutorial.webxml
+qtquick/webxml/qml-dynamicview-tutorial.webxml
+qtquick/webxml/qml-tutorial1.webxml
+qtquick/webxml/qml-tutorial2.webxml
+qtquick/webxml/qml-tutorial3.webxml
+qtquick/webxml/qml-tutorial.webxml
+qtquick/webxml/qquickitem-itemchangedata.webxml
+qtquick/webxml/qsggeometry-attribute.webxml
+qtquick/webxml/qsggeometry-coloredpoint2d.webxml
+qtquick/webxml/qsggeometry-point2d.webxml
+qtquick/webxml/qsggeometry-texturedpoint2d.webxml
+qtquick/webxml/qsgmaterialshader-graphicspipelinestate.webxml
+qtquick/webxml/qsgmaterialtype.webxml
+qtquick/webxml/qtquick-android-classes.webxml
+qtquick/webxml/qtquick-bestpractices.webxml
+qtquick/webxml/qtquick-codesamples.webxml
+qtquick/webxml/qtquick-convenience-topic.webxml
+qtquick/webxml/qtquick-cppextensionpoints.webxml
+qtquick/webxml/qtquick-effects-particles.webxml
+qtquick/webxml/qtquick-effects-sprites.webxml
+qtquick/webxml/qtquick-effects-topic.webxml
+qtquick/webxml/qtquick-effects-transformations.webxml
+qtquick/webxml/qtquick-how-tos.webxml
+qtquick/webxml/qtquick-input-focus.webxml
+qtquick/webxml/qtquick-input-mouseevents.webxml
+qtquick/webxml/qtquick-input-textinput.webxml
+qtquick/webxml/qtquick-input-topic.webxml
+qtquick/webxml/qtquicklayouts-overview.webxml
+qtquick/webxml/qtquicklayouts-responsive.webxml
+qtquick/webxml/qtquick-modelviewsdata-cppmodels.webxml
+qtquick/webxml/qtquick-modelviewsdata-modelview.webxml
+qtquick/webxml/qtquick-modelviewsdata-topic.webxml
+qtquick/webxml/qtquick-particles-performance.webxml
+qtquick/webxml/qtquick-positioning-anchors.webxml
+qtquick/webxml/qtquick-positioning-layouts.webxml
+qtquick/webxml/qtquick-positioning-righttoleft.webxml
+qtquick/webxml/qtquick-positioning-topic.webxml
+qtquick/webxml/qtquick-statesanimations-animations.webxml
+qtquick/webxml/qtquick-statesanimations-behaviors.webxml
+qtquick/webxml/qtquick-statesanimations-states.webxml
+qtquick/webxml/qtquick-statesanimations-topic.webxml
+qtquick/webxml/qtquick-tools-and-utilities.webxml
+qtquick/webxml/qtquickview-android-class.webxml
+qtquick/webxml/qtquick-visualcanvas-adaptations-openvg.webxml
+qtquick/webxml/qtquick-visualcanvas-adaptations-software.webxml
+qtquick/webxml/qtquick-visualcanvas-adaptations.webxml
+qtquick/webxml/qtquick-visualcanvas-coordinates.webxml
+qtquick/webxml/qtquick-visualcanvas-scenegraph-renderer.webxml
+qtquick/webxml/qtquick-visualcanvas-scenegraph.webxml
+qtquick/webxml/qtquick-visualcanvas-topic.webxml
+qtquick/webxml/qtquick-visualcanvas-visualparent.webxml
+qtquick/webxml/qtquick-visualtypes-topic.webxml
+qtquick/webxml/quick-changes-qt6.webxml
+qtremoteobjects/webxml/qtremoteobjects-cmake-qt-add-repc-merged.webxml
+qtremoteobjects/webxml/qtremoteobjects-cmake-qt-add-repc-replicas.webxml
+qtremoteobjects/webxml/qtremoteobjects-cmake-qt-add-repc-sources.webxml
+qtremoteobjects/webxml/qtremoteobjects-cmake-qt-rep-from-headers.webxml
+qtremoteobjects/webxml/qtremoteobjects-compatibility.webxml
+qtremoteobjects/webxml/qtremoteobjects-custom-transport.webxml
+qtremoteobjects/webxml/qtremoteobjects-external-schemas.webxml
+qtremoteobjects/webxml/qtremoteobjects-gettingstarted.webxml
+qtremoteobjects/webxml/qtremoteobjects-interaction.webxml
+qtremoteobjects/webxml/qtremoteobjects-node.webxml
+qtremoteobjects/webxml/qtremoteobjects-registry.webxml
+qtremoteobjects/webxml/qtremoteobjects-repc.webxml
+qtremoteobjects/webxml/qtremoteobjects-replica.webxml
+qtremoteobjects/webxml/qtremoteobjects-source.webxml
+qtremoteobjects/webxml/qtremoteobjects-troubleshooting.webxml
+qtremoteobjects/webxml/remoteobjects-changes-qt6.webxml
+qtscxml/webxml/qscxmlc.webxml
+qtscxml/webxml/qtscxml-changes-qt6.webxml
+qtscxml/webxml/qtscxml-cmake-qt-add-statecharts.webxml
+qtscxml/webxml/qtscxml-instantiating-state-machines.webxml
+qtscxml/webxml/qtscxml-overview.webxml
+qtscxml/webxml/qtscxml-scxml-compliance.webxml
+qtsensors/webxml/compatmap.webxml
+qtsensors/webxml/creating-a-sensor-plugin.webxml
+qtsensors/webxml/determining-the-default-sensor-for-a-type.webxml
+qtsensors/webxml/dynamic-sensor-backend-registration.webxml
+qtsensors/webxml/genericbackend.webxml
+qtsensors/webxml/qoutputrange.webxml
+qtsensors/webxml/qtsensors-changes-qt6.webxml
+qtsensors/webxml/qtsensors-cpp.webxml
+qtsensors/webxml/qtsensors-tutorial.webxml
+qtsensors/webxml/senorfwbackend.webxml
+qtsensors/webxml/sensors-backend-topics.webxml
+qtserialbus/webxml/qtcanbus-backends.webxml
+qtserialbus/webxml/qtmodbus-backends.webxml
+qtserialbus/webxml/qtserialbus-passthrucan-overview.webxml
+qtserialbus/webxml/qtserialbus-peakcan-overview.webxml
+qtserialbus/webxml/qtserialbus-socketcan-overview.webxml
+qtserialbus/webxml/qtserialbus-systeccan-overview.webxml
+qtserialbus/webxml/qtserialbus-tinycan-overview.webxml
+qtserialbus/webxml/qtserialbus-vectorcan-overview.webxml
+qtserialbus/webxml/qtserialbus-virtualcan-overview.webxml
+qtserialport/webxml/qtserialport-changes-qt6.webxml
+qtspatialaudio/webxml/spatialaudiooverview.webxml
+qtsql/webxml/qtsql-changes-qt6.webxml
+qtsql/webxml/sql-connecting.webxml
+qtsql/webxml/sql-driver.webxml
+qtsql/webxml/sql-forms.webxml
+qtsql/webxml/sql-model.webxml
+qtsql/webxml/sql-presenting.webxml
+qtsql/webxml/sql-programming.webxml
+qtsql/webxml/sql-sqlstatements.webxml
+qtsql/webxml/sql-types.webxml
+qtsvg/webxml/qtsvg-changes-qt6.webxml
+qtsvg/webxml/svgextensions.webxml
+qtsvg/webxml/svgrendering.webxml
+qttestlib/webxml/qtest-overview.webxml
+qttestlib/webxml/qtest-tutorial.webxml
+qttestlib/webxml/qttest-best-practices-qdoc.webxml
+qttestlib/webxml/qttestlib-tutorial6.webxml
+qttestlib/webxml/testlib-changes-qt6.webxml
+qttexttospeech/webxml/qttexttospeech-changes-qt6.webxml
+qttexttospeech/webxml/qttexttospeech-engines.webxml
+qtwebchannel/webxml/qtwebchannel-changes-qt6.webxml
+qtwebchannel/webxml/qtwebchannel-javascript.webxml
+qtwebengine/webxml/qtwebengine-changes-qt6.webxml
+qtwebengine/webxml/qtwebengine-features.webxml
+qtwebengine/webxml/qtwebengine-overview.webxml
+qtwebengine/webxml/qtwebengine-platform-notes.webxml
+qtwebengine/webxml/qtwebenginewidgets-qtwebkitportingguide.webxml
+qtwebengine/webxml/qwebenginecookiestore-filterrequest.webxml
+qtwebsockets/webxml/echoclient.webxml
+qtwebsockets/webxml/echoserver.webxml
+qtwebsockets/webxml/qtwebsockets-changes-qt6.webxml
+qtwebsockets/webxml/qtwebsockets-testing.webxml
+qtwebsockets/webxml/websockets-overview.webxml
+qtwidgets/webxml/application-windows.webxml
+qtwidgets/webxml/dialogs.webxml
+qtwidgets/webxml/focus.webxml
+qtwidgets/webxml/gallery.webxml
+qtwidgets/webxml/gestures-overview.webxml
+qtwidgets/webxml/graphicsview.webxml
+qtwidgets/webxml/layout.webxml
+qtwidgets/webxml/mainwindow.webxml
+qtwidgets/webxml/model-view-programming.webxml
+qtwidgets/webxml/modelview.webxml
+qtwidgets/webxml/qdrawutil-h.webxml
+qtwidgets/webxml/qformlayout-takerowresult.webxml
+qtwidgets/webxml/qtextedit-extraselection.webxml
+qtwidgets/webxml/qtilerules.webxml
+qtwidgets/webxml/qwidget-styling.webxml
+qtwidgets/webxml/style-reference.webxml
+qtwidgets/webxml/stylesheet-customizing.webxml
+qtwidgets/webxml/stylesheet-designer.webxml
+qtwidgets/webxml/stylesheet-reference.webxml
+qtwidgets/webxml/stylesheet-syntax.webxml
+qtwidgets/webxml/stylesheet.webxml
+qtwidgets/webxml/widget-classes.webxml
+qtwidgets/webxml/widgets-changes-qt6.webxml
+qtwidgets/webxml/widgets-tutorial.webxml
+qtxml/webxml/xml-changes-qt6.webxml
+qtxml/webxml/xml-dom-tml.webxml
+qtxml/webxml/xml-namespaces.webxml
+qtxml/webxml/xml-processing.webxml
+qtxml/webxml/xml-streaming.webxml
+qtxml/webxml/xml-tools.webxml
+
+# WebXML <group> files (lists of classes grouped by function/curated)
+[groups]
+qtcore/webxml/animation.webxml
+qtcore/webxml/events.webxml
+qtcore/webxml/io.webxml
+qtgui/webxml/painting.webxml
+qtgui/webxml/painting-3d.webxml
+qtgui/webxml/richtext-processing.webxml
+qtprintsupport/webxml/printing.webxml
+qtsql/webxml/database.webxml
+qtlocation/webxml/qml-qtlocation5-maps.webxml
+qtquick/webxml/qtquick-scenegraph-nodes.webxml
+qtquick/webxml/qtquick-scenegraph-materials.webxml
+qtnetwork/webxml/network.webxml
These are the main modules that help you build a Widget-based UI.
-.. grid:: 1 3 3 3
+.. grid:: 1 3 4 4
:gutter: 2
- .. grid-item-card:: :mod:`QtCore <PySide6.QtCore>`
+ .. grid-item-card:: :mod:`Qt Core <PySide6.QtCore>`
- Provides core non-GUI functionality, like signal and slots, properties,
- base classes of item models, serialization, and more.
+ Core non-graphical classes used by other modules.
- .. grid-item-card:: :mod:`QtGui <PySide6.QtGui>`
+ .. grid-item-card:: :mod:`Qt GUI <PySide6.QtGui>`
- Extends QtCore with GUI functionality: Events, windows and screens,
- OpenGL and raster-based 2D painting, as well as images.
+ Base classes for graphical user interface (GUI) components.
- .. grid-item-card:: :mod:`QtWidgets <PySide6.QtWidgets>`
+ .. grid-item-card:: :mod:`Qt Widgets <PySide6.QtWidgets>`
- Provides ready to use Widgets for your application, including graphical
- elements for your UI.
+ Classes to extend Qt GUI with Python widgets.
QML and Qt Quick
----------------
-Use these modules to interact with the `QML Language <https://doc.qt.io/qt-5.qmlapplications>`_,
+Use these modules to interact with the `QML Language <https://doc.qt.io/qt-6/qmlapplications.html>`_,
from Python.
-.. grid:: 1 3 3 3
+.. grid:: 1 3 4 4
:gutter: 2
- .. grid-item-card:: :mod:`QtQml <PySide6.QtQml>`
+ .. grid-item-card:: :mod:`Qt Qml <PySide6.QtQml>`
- The base Python API to interact with the module.
+ Classes for QML and JavaScript languages.
- .. grid-item-card:: :mod:`QtQuick <PySide6.QtQuick>`
+ .. grid-item-card:: :mod:`Qt Quick <PySide6.QtQuick>`
- Provides classes to embed Qt Quick in Qt applications.
+ A declarative framework for building highly dynamic applications
+ with custom UIs.
- .. grid-item-card:: :mod:`QtQuickWidgets <PySide6.QtQuickWidgets>`
+ .. grid-item-card:: :mod:`Qt Quick Controls <PySide6.QtQuickControls2>`
- Provides the QQuickWidget class to embed Qt Quick in widget-based
- applications.
+ Lightweight QML types for creating performant user interfaces for
+ desktop, embedded, and mobile devices.
+
+ .. grid-item-card:: :mod:`Qt Quick Widgets <PySide6.QtQuickWidgets>`
+
+ Provides a Python widget class for displaying a Qt Quick user interface.
All the modules
---------------
--- /dev/null
+.. _buildingfromsource:
+
+Building from Source
+====================
+
+.. toctree::
+ :maxdepth: 1
+ :hidden:
+
+ linux.rst
+ macOS.rst
+ windows.rst
+
+.. important:: This page is focused on building |project| **from source**.
+ If you just want to install |pymodname|, you need to run: :command:`pip install pyside6`.
+
+ For more details, refer to our :ref:`getting-started` guide. Additionally, you can check the
+ :ref:`FAQ <faq>` related to the project.
+
+.. _Quick Start: gettingstarted.html
+
+General Requirements
+--------------------
+
+The following prerequisites must be installed before you build |project|.
+On **Linux** you might get them with your operating system package manager, on **macOS**
+you might get them with ``brew``, and on **Windows** you can download the installer from each
+website.
+
+* **Python**: 3.9+ `[official Python website] <https://www.python.org/downloads/>`_
+* **Qt:** 6.8+ `[online installer] <https://download.qt.io/official_releases/online_installers/>`_
+* **CMake:** 3.18+ `[official CMake website] <https://cmake.org/download/>`_
+* **Git:** 2.0+. `[official Git website] <https://git-scm.com/downloads>`_
+* **libclang:** The libclang library, recommended: version 18+ for 6.8+.
+ Prebuilt versions for each OS can be `downloaded here`_.
+* Check the `Supported Platforms of Qt`_
+
+.. _downloaded here: https://download.qt.io/development_releases/prebuilt/libclang/
+.. _`Supported Platforms of Qt` : https://doc.qt.io/qt-6/supported-platforms.html
+
+Guides per platform
+-------------------
+
+You can refer to the following pages for platform specific instructions:
+
+.. grid:: 1 3 3 3
+ :gutter: 2
+
+ .. grid-item-card::
+ :img-top: ../images/windows.svg
+ :class-item: text-center
+
+ +++
+ .. button-ref:: windows
+ :color: primary
+ :outline:
+ :expand:
+
+ Windows
+
+ .. grid-item-card::
+ :img-top: ../images/macos.svg
+ :class-item: text-center
+
+ +++
+ .. button-ref:: macOS
+ :color: primary
+ :outline:
+ :expand:
+
+ macOS
+
+ .. grid-item-card::
+ :img-top: ../images/linux.svg
+ :class-item: text-center
+
+ +++
+ .. button-ref:: linux
+ :color: primary
+ :outline:
+ :expand:
+
+ Linux
+
+.. important:: |project| does not yet support WebAssembly and the mobile operating systems (Android or iOS).
+ Most Linux-based embedded OS provide PySide with their official
+ package manager (for example, `Raspbian`_ and `ArchlinuxARM`_).
+
+.. _Raspbian: https://www.raspbian.org/
+.. _ArchlinuxARM: https://archlinuxarm.org/
+
+A normal building command will look like this::
+
+ python setup.py install --qtpaths=/path/to/qtpaths \
+ --ignore-git \
+ --debug \
+ --build-tests \
+ --parallel=8 \
+ --verbose-build \
+ --module-subset=Core,Gui,Widgets
+
+Which will build and install the project with **debug** symbols, including the **tests**,
+using **ninja** (instead of make), and considering only the **module subset** of
+:mod:`QtCore <PySide6.QtCore>`, :mod:`QtGui <PySide6.QtGui>`, and
+:mod:`QtWidgets <PySide6.QtWidgets>`.
+
+`CMake Unity Build Mode`_ is used by default for speed-up.
+
+Other important options to consider are:
+ * ``--cmake``, to specify the path to the cmake binary,
+ * ``--reuse-build``, to rebuild only the modified files,
+ * ``--openssl=/path/to/openssl/bin``, to use a different path for OpenSSL,
+ * ``--standalone``, to copy over the Qt libraries into the final package to make it work on other
+ machines,
+ * ``--build-docs``, to enable the documentation build.
+ * ``--doc-build-online``, to build documentation using the online template (by
+ default it uses the offline)
+
+Testing the installation
+------------------------
+
+Once the installation finishes, you will be able to execute any of our examples::
+
+ python examples/widgets/widgets/tetrix/tetrix.py
+
+Running Tests
+-------------
+
+Using the ``--build-tests`` option will enable us to run all the auto tests inside the project::
+
+ python testrunner.py test > testlog.txt
+
+.. note:: On Windows, don't forget to have qtpaths in your path
+ (:command:`set PATH=C:\\\Path\\\to\\\Qt\\\6.4.0\\\msvc2019_64\\\bin;%PATH%`)
+
+You can also run a specific test (for example ``qpainter_test``) by running::
+
+ ctest -R qpainter_test --verbose
+
+.. _cross_compilation:
+
+Cross Compilation
+-----------------
+
+Starting from 6.3, it is possible to cross-compile Shiboken (module), and
+PySide. This functionality is still in Technical Preview, which means it could
+change in the future releases.
+
+.. important:: The only supported configuration is using a host Linux
+ machine to cross-compile to a Linux target platform.
+
+Cross compiling software is a valid use case that many projects rely on,
+however, it is a complicated process that might fail due to many reasons.
+
+Before starting with the process, it is important to understand the details of
+the build system, and the goal of cross compilation.
+
+In the build process, a ``Host`` is the computer you are currently using to
+compile, and a ``Target`` is your embedded device that you are compiling for.
+
+Qt for Python is being built using setuptools, and relies on a ``setup.py`` file
+that is called recursively to build Shiboken (module),
+Shiboken (generator), and PySide. As the generator is creating
+the wrappers for the bindings, it's not cross compiled
+for the target.
+Only the Shiboken (module) and PySide are cross compiled.
+
+The building process requires a Qt installation, and a Python interpreter
+on both the host, and the target. The used Qt versions on both platforms
+should have the same minor version. That is, Qt 6.3 (host)
+cannot be used with a Qt 6.2 (target), or the other way around.
+
+.. note:: It is recommended to use the same version of Qt on both host and target to avoid possible
+ unconventional errors.
+
+Prerequisites
+~~~~~~~~~~~~~
+
+First and foremost, you need to have access to the target device because you
+need to copy several system files (sysroot). We recommend a Linux OS that has
+the latest Qt versions, like `Manjaro ARM`_ or `Archlinux ARM`_.
+
+* (target) Install Qt 6.3+ on the system using the package manager.
+* (host) Install Qt 6.3+ on the system using the package manager or Qt
+ Installer.
+* (target, host) Install the library and development packages that provide
+ C++ headers, linkers, libraries, and compilers.
+* (target, host) Install Python interpreter v3.7 or later
+* (target, host) Install CMake 3.17+
+
+After installing these prerequisites, copy the ``target`` sysroot to your
+``host`` computer. This process is tricky, because copying system files from
+another computer might cause problems with the symbolic links. Here you
+have two options to achieve that.
+
+Option A: Copying the files
+***************************
+
+Create a directory to copy the sysroot of your target device,
+for example ``rpi-sysroot``, and perform the copy on your host computer:
+
+.. code-block:: bash
+
+ rsync -vR --progress -rl --delete-after --safe-links \
+ USERNAME@TARGET_IP:/{lib,usr,opt/vc/lib} rpi-sysroot/
+
+Ensure to replace ``USERNAME`` and ``TARGET_IP`` with your system appropriate
+values.
+
+Option B: Packaging the file system
+***********************************
+
+Create a package for your sysroot in your target:
+
+.. code-block:: bash
+
+ tar cfJ ~/sysroot.tar.xz /lib /usr /opt/vc/lib
+
+Copy the package from the target to your host:
+
+.. code-block:: bash
+
+ rsync -vR --progress USERNAME@TARGET_IP:sysroot.tar.xz .
+
+Once you have the tar file, unpack it inside a ``rpi-sysroot`` directory.
+
+Fix Symlinks
+************
+
+It is recommended to run the following script to fix
+most of the issues you would find with symbolic links:
+
+.. code-block:: python
+
+ import sys
+ from pathlib import Path
+ import os
+
+ # Take a sysroot directory and turn all the absolute symlinks and turn them into
+ # relative ones such that the sysroot is usable within another system.
+
+ if len(sys.argv) != 2:
+ print(f"Usage is {sys.argv[0]} <sysroot-directory>")
+ sys.exit(-1)
+
+ topdir = Path(sys.argv[1]).absolute()
+
+ def handlelink(filep, subdir):
+ link = filep.readlink()
+ if str(link)[0] != "/":
+ return
+ if link.startswith(topdir):
+ return
+ relpath = os.path.relpath((topdir / link).resolve(), subdir)
+ os.unlink(filep)
+ os.symlink(relpath, filep)
+
+ for f in topdir.glob("**/*"):
+ if f.is_file() and f.is_symlink():
+ handlelink(f, f.parent)
+
+Setting up the toolchain
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+To perform the cross compilation, you need a special set of compilers,
+libraries, and headers, which runs on the host architecture, but generates
+(binaries/executables) for a target architecture.
+For example, from x86_64 to aarch64.
+
+It is recommended to use the official 10.2 `ARM Developer cross compilers`_,
+which you can find on their official website. For this tutorial, we choose
+``aarch64`` target architecture and we will assume that you downloaded the
+`gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu.tar.xz`_ file,
+and unpacked it.
+
+With those compilers, now you need a CMake toolchain file. This is
+a configuration file to set the compilers and sysroot information, together
+with extra options like compilation flags, and other details. You can use the
+following file as an example, but keep in mind they might vary:
+
+.. code-block:: cmake
+
+ # toolchain-aarch64.cmake
+ cmake_minimum_required(VERSION 3.18)
+ include_guard(GLOBAL)
+
+ set(CMAKE_SYSTEM_NAME Linux)
+ set(CMAKE_SYSTEM_PROCESSOR aarch64)
+
+ set(TARGET_SYSROOT /path/to/your/target/sysroot)
+ set(CROSS_COMPILER /path/to/your/crosscompiling/compilers/)
+
+ set(CMAKE_SYSROOT ${TARGET_SYSROOT})
+
+ set(ENV{PKG_CONFIG_PATH} "")
+ set(ENV{PKG_CONFIG_LIBDIR} ${CMAKE_SYSROOT}/usr/lib/pkgconfig:${CMAKE_SYSROOT}/usr/share/pkgconfig)
+ set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT})
+
+ set(CMAKE_C_COMPILER ${CROSS_COMPILER}/aarch64-none-linux-gnu-gcc)
+ set(CMAKE_CXX_COMPILER ${CROSS_COMPILER}/aarch64-none-linux-gnu-g++)
+
+ set(QT_COMPILER_FLAGS "-march=armv8-a")
+ set(QT_COMPILER_FLAGS_RELEASE "-O2 -pipe")
+ set(QT_LINKER_FLAGS "-Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed")
+
+ set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+
+ include(CMakeInitializeConfigs)
+
+ function(cmake_initialize_per_config_variable _PREFIX _DOCSTRING)
+ if (_PREFIX MATCHES "CMAKE_(C|CXX|ASM)_FLAGS")
+ set(CMAKE_${CMAKE_MATCH_1}_FLAGS_INIT "${QT_COMPILER_FLAGS}")
+
+ foreach (config DEBUG RELEASE MINSIZEREL RELWITHDEBINFO)
+ if (DEFINED QT_COMPILER_FLAGS_${config})
+ set(CMAKE_${CMAKE_MATCH_1}_FLAGS_${config}_INIT "${QT_COMPILER_FLAGS_${config}}")
+ endif()
+ endforeach()
+ endif()
+
+ if (_PREFIX MATCHES "CMAKE_(SHARED|MODULE|EXE)_LINKER_FLAGS")
+ foreach (config SHARED MODULE EXE)
+ set(CMAKE_${config}_LINKER_FLAGS_INIT "${QT_LINKER_FLAGS}")
+ endforeach()
+ endif()
+
+ _cmake_initialize_per_config_variable(${ARGV})
+ endfunction()
+
+You need to adjust the paths in these two lines::
+
+ set(TARGET_SYSROOT /path/to/your/target/sysroot)
+ set(CROSS_COMPILER /path/to/your/crosscompiling/compilers/)
+
+and replace them with the sysroot directory (the one we called ``rpi-sysroot``),
+and the compilers (the ``gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu/bin`` directory).
+
+
+Cross compiling PySide
+~~~~~~~~~~~~~~~~~~~~~~
+
+After you have installed the prerequisites and copied the necessary files, you
+should have the following:
+
+* The compilers to cross compile (``gcc-argm-10.2-...``),
+* The target sysroot (``rpi-sysroot``),
+* The toolchain cmake file (``toolchain-aarch64.cmake``),
+* The ``pyside-setup`` repository,
+
+An example of the ``setup.py`` invocation might look like the following:
+
+.. code-block:: bash
+
+ python setup.py bdist_wheel \
+ --parallel=8 --ignore-git --reuse-build --standalone --limited-api=yes \
+ --cmake-toolchain-file=/opt/toolchain-aarch64.cmake \
+ --qt-host-path=/opt/Qt/6.3.0/gcc_64 \
+ --plat-name=linux_aarch64
+
+Depending on the target platform, you could use ``linux_armv7``,
+``linux_aarch64``, etc.
+
+For embedded platforms, which typically do not have Qt and its tools fully
+built, the option ``--no-qt-tools`` can be used to prevent the bundling of the
+tools.
+
+If the process succeeds, you will find the target wheels in your ``dist/``
+directory, for example:
+
+.. code-block:: bash
+
+ PySide6-6.3.0-6.3.0-cp36-abi3-manylinux2014_aarch64.whl
+ shiboken6-6.3.0-6.3.0-cp36-abi3-manylinux2014_aarch64.whl
+
+
+Troubleshooting
+***************
+
+* If the auto-detection mechanism fails to find the Python or Qt installations
+ you have in your target device, you can use two additional options::
+
+ --python-target-path=...
+
+ and::
+
+ --qt-target-path=...
+
+* In case the automatic build of the host Shiboken (generator) fails,
+ you can specify the custom path using::
+
+ --shiboken-host-path=...
+
+.. _`Manjaro ARM`: https://manjaro.org/products/download/arm
+.. _`Archlinux ARM`: https://archlinuxarm.org
+.. _`ARM Developer Cross Compilers`: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-a/downloads
+.. _`gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu.tar.xz`: https://developer.arm.com/-/media/Files/downloads/gnu-a/10.2-2020.11/binrel/gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu.tar.xz
+
+.. _building_documentation:
+
+Building the documentation
+--------------------------
+
+Starting from 6.3, the documentation is not being built by default.
+When using the second option described in this section, make sure to use the
+``--build-docs`` option.
+
+Install the specific documentation requirements in your Python virtual
+environment::
+
+ pip install -r requirements-doc.txt
+
+You can find the ``requirements-doc.txt`` file in the root of the repository.
+
+Starting from 5.15, there are two options to build the documentation:
+
+1. Building the base documentation (no API)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The process of parsing Qt headers to generate the PySide API documentation can take several
+minutes, this means that modifying a specific section of the documentation we currently have, might
+become a hard task. You may only care about the base documentation, which comprises all the
+documentation except for the API documentation.
+
+To generate this, execute the following command::
+
+ python setup.py build_base_docs
+
+This will generate an ``html/`` directory with the following structure::
+
+ html
+ └── pyside6
+ ├── index.html
+ ├── ...
+ └── shiboken6
+ ├── index.html
+ └── ...
+
+so you can open the main page ``html/pyside6/index.html`` on your browser to check the generated
+files.
+
+This is useful when updating the general sections of the documentation, adding tutorials,
+modifying the build instructions, and more.
+
+.. note:: In case you are interested in only generating the Example Gallery,
+ you would need to run ``python tools/example_gallery/main.py`` to
+ generate the examples ``documentation`` for the gallery. This will
+ also be used internally by the ``build_base_docs`` target
+
+
+2. Building the documentation (Base + API)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The documentation is being generated using **qdoc** to get the API information, and also **sphinx**
+for the local Python related notes.
+
+The system requires the ``libxml2`` and ``libxslt`` libraries.
+
+After installing ``graphviz`` (included in the ``requirements-doc.txt`` file),
+the ``dot`` command needs to be in PATH, otherwise,
+the process will fail. Installing ``graphviz`` system-wide is also an option.
+
+Since the process relies on a Qt installation, you need to specify where the
+``qtbase`` directory of a Qt source tree is located by passing it to
+the command line option ``--qt-src-dir``.
+
+Once the common ``setup.py`` build process finishes (remember to use
+``--build-docs`` to enable the documentation build, and ``--doc-build-online``
+to get the HTML files), you can go to the generated
+``build/<your_env_name>/build/pyside6`` directory, and run::
+
+ ninja apidoc
+
+You can add ``-j X``, to perform the build process in parallel with
+X processes.
+
+.. note:: The :command:`apidoc` make target builds offline documentation in ``QCH``
+ (Qt Compressed Help) format by default. You can switch to building for the
+ online use with the ``--doc-build-online`` configure option.
+
+The target executes several steps:
+
+#. The ``qdoc`` tool is run over the Qt source code to produce documentation in WebXML format.
+#. ``shiboken6`` is run to extract the functions for which bindings exist from WebXML and convert it into RST.
+#. ``sphinx`` is run to produce the documentation in HTML format.
+
+Re-running the command will not execute step 1 unless the file
+``qdoc-output/webxml/qtcore-index.webxml`` is removed from the build tree.
+Similarly, step 2 will not be executed unless the file ``base/PySide6/QtCore/index.rst``
+is removed.
+
+Finally, you will get a ``html`` directory containing all the generated documentation. The offline
+help files, ``PySide.qch`` and ``Shiboken.qch``, can be moved to any directory of your choice. You
+can find ``Shiboken.qch`` in the build directory, ``build/<your_env_name>/build/shiboken6/doc/html``.
+
+If you want to temporarily change a ``.rst`` file to examine the impact on
+formatting, you can re-run ``sphinx`` in the ``doc`` directory::
+
+ sphinx-build base html
+
+Viewing offline documentation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The offline documentation (QCH) can be viewed using the *Qt Creator* IDE or
+*Qt Assistant*, which is a standalone application for viewing QCH files.
+
+To view the QCH using *Qt Creator*, following the instructions outlined in
+`Using Qt Creator Help Mode <https://doc.qt.io/qtcreator/creator-how-to-add-external-documentation.html>`_.
+If you chose to use *Qt Assistant* instead, use the following command to register
+the QCH file before launching *Qt Assistant*::
+
+ assistant -register PySide.qch
+
+Troubleshooting documentation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The documentation uses intersphinx to link from the PySide to the
+Shiboken documentation. This can fail if
+
+* the default ``QCH`` format is used; in which case the required ``objects.inv``
+ files are not generated. Use ``--doc-build-online``.
+* base and full doc builds are mixed, resulting in wrong values for the
+ intersphinx location in the CMake files. Re-run ``cmake`` to fix this.
+
+Using the internal tools
+------------------------
+
+A set of tools can be found under the ``tools/`` directory inside the ``pyside-setup`` repository.
+
+* ``checklibs.py``: Script to analyze dynamic library dependencies of Mach-O binaries.
+ To use this utility, just run::
+
+ python checklibs.py /path/to/some.app/Contents/MacOS/Some
+
+ This script was fetched from this repository_.
+
+* ``create_changelog.py``: Script used to create the CHANGELOG that you can find in the ``dist/``
+ directory. Usage::
+
+ python create_changelog.py -r 6.0.1 -v v6.0.0..6.0 -t bug-fix
+
+* ``debug_windows.py``: This script can be used to find out why PySide modules
+ fail to load with various DLL errors like Missing DLL or Missing symbol in DLL.
+
+ You can think of it as a Windows version of :command:`ldd` / ``LD_DEBUG``.
+
+ Underneath, it uses the :command:`cdb.exe` command line debugger and the :command:`gflags.exe`
+ tool, which are installed with the latest Windows Kit.
+
+ The aim is to help developers debug issues that they may encounter using the PySide imports on
+ Windows. The user should then provide the generated log file.
+
+ Incidentally it can also be used for any Windows executables, not just Python.
+ To use it just run::
+
+ python debug_windows.py
+
+* ``missing_bindings.py``: This script is used to compare the state of PySide and PyQt
+ regarding available modules and classses. This content is displayed in our `wiki page`_,
+ and can be used as follows::
+
+ python missing_bindings.py --qt-version 6.0.1 -w all
+
+.. note:: The script relies on BeautifulSoup to parse the content and generate a list of the
+ missing bindings.
+
+.. _repository: https://github.com/liyanage/macosx-shell-scripts/
+.. _`wiki page`: https://wiki.qt.io/Qt_for_Python_Missing_Bindings
+.. _BeautifulSoup: https://www.crummy.com/software/BeautifulSoup/
+.. _`CMake Unity Build Mode` : https://cmake.org/cmake/help/latest/prop_tgt/UNITY_BUILD.html
--- /dev/null
+Linux
+=====
+
+Requirements
+------------
+
+* GCC
+* ``sphinx`` package for the documentation (optional).
+* Depending on your linux distribution, the following dependencies might also be required:
+
+ * ``libgl-dev``, ``python-dev``, and ``python-setuptools``.
+* Check the platform dependencies of `Qt for Linux/X11`_.
+
+Building from source
+--------------------
+
+Creating a virtual environment
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``venv`` module allows you to create a local, user-writeable copy of a python environment into
+which arbitrary modules can be installed and which can be removed after use::
+
+ python -m venv testenv
+ source testenv/bin/activate
+
+will create and use a new virtual environment, which is indicated by the command prompt changing.
+
+Alternatively, you can use the `uv`_ tool, which is popular in the Python community for Python
+project management. The following command creates a virtual environment using `uv`_::
+
+ uv venv --python <python-version> testenv
+ source testenv/bin/activate
+
+.. note:: Since the Qt for Python project still uses `setup.py` and not `pyproject.toml`, currently
+ `uv` can only be used as a replacement for `pyenv` for building Qt for Python. If you
+ have already the `.python-version` file (used by .pyenv) in the project, make sure to
+ change the version to the `uv`_ Python you want to use.
+
+Setting up CLANG
+~~~~~~~~~~~~~~~~
+
+If you don't have libclang already in your system, you can download from the Qt servers::
+
+ wget https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_18.1.5-based-linux-Rhel8.6-gcc10.3-x86_64.7z
+
+Extract the files, and leave it in any desired path, and set the environment
+variable required::
+
+ 7z x libclang-release_18.1.5-based-linux-Rhel8.6-gcc10.3-x86_64.7z
+ export LLVM_INSTALL_DIR=$PWD/libclang
+
+Getting the source
+~~~~~~~~~~~~~~~~~~
+
+Cloning the official repository can be done by::
+
+ git clone https://code.qt.io/pyside/pyside-setup
+
+Checking out the version that we want to build, for example 6.8::
+
+ cd pyside-setup && git checkout 6.8
+
+Install the general dependencies::
+
+ pip install -r requirements.txt
+
+For building the documentation::
+
+ pip install -r requirements-doc.txt
+
+.. note:: Keep in mind you need to use the same version as your Qt installation.
+ Additionally, :command:`git checkout -b 6.8 --track origin/6.8` could be a better option
+ in case you want to work on it.
+
+.. note:: With `uv`_, use `uv pip install ...`
+
+Building and Installing (setuptools)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``setuptools`` approach uses the ``setup.py`` file to execute the build step and further
+uses `create_wheels.py`_ to create the wheels. Once the wheels are created, you can install the
+wheels using the `pip` command.
+
+Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
+for example, :command:`/opt/Qt/6.8.0/gcc_64/bin/qtpaths`.
+
+Build can take a few minutes, so it is recommended to use more than one CPU core::
+
+ python setup.py build --qtpaths=/opt/Qt/6.8.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
+
+With `uv`_, the build command becomes::
+
+ uv run setup.py build --qtpaths=/opt/Qt/6.8.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
+
+To create the wheels, just run::
+
+ python create_wheels.py --build-dir=/directory/where/pyside/is/built --no-examples
+
+On successful completion, the wheels will be created in the `dist` directory.
+
+.. note:: The `build-dir` typically looks like `build/<your_python_environment_name>`. The
+ requirement is that this `build-dir` should contain the `packages_for_wheel` directory.
+ If the `python setup.py` build command was successful, this directory should be present.
+
+Finally, to install the wheels, use the following command::
+
+ pip install dist/*.whl
+
+Building and Installing (cmake)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``setuptools`` approach includes internal ``CMake`` calls when
+building and installing the project, but a CMake-only approach is only
+recommended for packaging the project for distribution builds.
+
+Assuming that Qt is in PATH, for example, the configure step can be done with::
+
+ cmake -B /path/to/the/build/directory \
+ -S /path/to/the/pyside-setup \
+ -DCMAKE_INSTALL_PREFIX=/where/to/install \
+ -DPython_EXECUTABLE=/path/to/interpreter
+
+.. note:: You can add `-DFORCE_LIMITED_API=yes` in case you want to have a
+ build which will be compatible with Python 3.8+.
+
+and then for building::
+
+ cmake --build /path/to/the/build/directory --parallel X
+
+where `X` is the number of processes you want to use.
+Finally, the install step can be done with::
+
+ cmake --install /path/to/the/build/directory
+
+.. note:: You can build only pyside6 or only shiboken6 by using
+ the different source directories with the option `-S`.
+
+
+Test installation
+~~~~~~~~~~~~~~~~~
+
+You can execute one of the examples to verify the process is properly working.
+Remember to properly set the environment variables for Qt and PySide::
+
+ python examples/widgets/widgets/tetrix/tetrix.py
+
+.. _`Qt for Linux/X11`: https://doc.qt.io/qt-6/linux.html
+.. _`uv`: https://docs.astral.sh/uv/
+.. _`create_wheels.py`: https://code.qt.io/cgit/pyside/pyside-setup.git/tree/create_wheels.py
--- /dev/null
+macOS
+=====
+
+Requirements
+------------
+
+* `Xcode`_ 8.2 (macOS 10.11), 8.3.3 (macOS 10.12), 9 (macOS 10.13), 10.1 (macOS 10.14)
+* ``sphinx`` package for the documentation (optional).
+* Depending on your OS, the following dependencies might also be required:
+
+ * ``libgl-dev``, ``python-dev``, and ``python-setuptools``.
+
+* Check the platform dependencies of `Qt for macOS`_.
+
+.. _Xcode: https://developer.apple.com/xcode/
+.. _`Qt for macOS`: https://doc.qt.io/qt-6/macos.html
+
+Building from source
+--------------------
+
+Creating a virtual environment
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``venv`` module allows you to create a local, user-writeable copy of a python environment into
+which arbitrary modules can be installed and which can be removed after use::
+
+ python -m venv testenv # your interpreter could be called 'python3'
+ source testenv/bin/activate
+
+will create and use a new virtual environment, which is indicated by the command prompt changing.
+
+Alternatively, you can use the `uv`_ tool, which is popular in the Python community for Python
+project management. The following command creates a virtual environment using `uv`_::
+
+ uv venv --python <python-version> testenv
+ source testenv/bin/activate
+
+.. note:: Since the Qt for Python project still uses `setup.py` and not `pyproject.toml`, currently
+ `uv` can only be used as a replacement for `pyenv` for building Qt for Python. If you
+ have already the `.python-version` file (used by `pyenv`) in the project, make sure to
+ change the version to the `uv`_ Python you want to use.
+
+Setting up CLANG
+~~~~~~~~~~~~~~~~
+
+If you don't have `libclang` already in your system, you can download from the Qt servers::
+
+ wget https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_18.1.5-based-macos-universal.7z
+
+Extract the files, and leave it in any desired path, and set the environment
+variable required::
+
+ 7z x libclang-release_18.1.5-based-macos-universal.7z
+ export LLVM_INSTALL_DIR=$PWD/libclang
+
+Getting PySide
+~~~~~~~~~~~~~~
+
+Cloning the official repository can be done by::
+
+ git clone https://code.qt.io/pyside/pyside-setup
+
+Checking out the version that we want to build, for example, 6.8::
+
+ cd pyside-setup && git checkout 6.8
+
+Install the general dependencies::
+
+ pip install -r requirements.txt
+
+For building the documentation::
+
+ pip install -r requirements-doc.txt
+
+.. note:: Keep in mind you need to use the same version as your Qt installation
+
+.. note:: With `uv`_, use `uv pip install ...
+
+Building PySide
+~~~~~~~~~~~~~~~
+
+Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
+for example, ``/opt/Qt/6.8.0/gcc_64/bin/qtpaths``.
+
+Build can take a few minutes, so it is recommended to use more than one CPU core::
+
+ python setup.py build --qtpaths=/opt/Qt/6.8.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
+
+With `uv`_, this command becomes::
+
+ uv run setup.py build --qtpaths=/opt/Qt/6.8.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
+
+
+Installing PySide
+~~~~~~~~~~~~~~~~~
+
+First, create the wheels using the `create_wheels.py`_ script::
+
+ python create_wheels.py --build-dir=/directory/where/pyside/is/built --no-examples
+
+On successful completion, the wheels will be created in the `dist` directory.
+
+.. note:: The `build-dir` typically looks like `build/<your_python_environment_name>`. The
+ requirement is that this `build-dir` should contain the `packages_for_wheel` directory.
+ If the `python setup.py` build command was successful, this directory should be present.
+
+Finally, to install the wheels, use the following command::
+
+ pip install dist/*.whl
+
+Test installation
+~~~~~~~~~~~~~~~~~
+
+You can execute one of the examples to verify the process is properly working.
+Remember to properly set the environment variables for Qt and PySide::
+
+ python examples/widgets/widgets/tetrix/tetrix.py
+
+.. _`uv`: https://docs.astral.sh/uv/
+.. _`create_wheels.py`: https://code.qt.io/cgit/pyside/pyside-setup.git/tree/create_wheels.py
+
--- /dev/null
+Windows
+=======
+
+The Qt library has to be built with the same version of MSVC as Python and PySide, this can be
+selected when using the online installer.
+
+Requirements
+------------
+
+* `MSVC2022`_ for Python 3 on Windows,
+* `OpenSSL`_ (optional for SSL support, Qt must have been configured using the same SSL library).
+* ``sphinx`` package for the documentation (optional).
+* Check the platform dependencies of `Qt for Windows`_.
+
+.. note:: The Python provided by the Microsoft Store is not compatible with PySide. Please
+ use https://www.python.org/downloads/ to get a Python Interpreter.
+
+.. _MSVC2022: https://visualstudio.microsoft.com/downloads/
+.. _OpenSSL: https://sourceforge.net/projects/openssl/
+.. _`Qt for Windows`: https://doc.qt.io/qt-6/windows.html
+
+Building from source on Windows 10
+----------------------------------
+
+Creating a virtual environment
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``venv`` module allows you to create a local, user-writable copy of a python environment into
+which arbitrary modules can be installed and which can be removed after use::
+
+ python -m venv testenv
+ call testenv\Scripts\activate
+
+will create and use a new virtual environment, which is indicated by the command prompt changing.
+
+Alternatively, you can use the `uv`_ tool, which is popular in the Python community for Python
+project management. The following command creates a virtual environment using `uv`_::
+
+ uv venv --python <python-version> testenv
+ call testenv\Scripts\activate
+
+.. note:: Since the Qt for Python project still uses `setup.py` and not `pyproject.toml`, currently
+ `uv` can only be used as a replacement for `pyenv` for building Qt for Python. If you
+ have already the `.python_version` file (used by .pyenv) in the project, make sure to
+ change the version to the `uv`_ Python you want to use.
+
+Setting up CLANG
+~~~~~~~~~~~~~~~~
+
+libclang can be downloaded from the
+`Qt servers <https://download.qt.io/development_releases/prebuilt/libclang>`_.
+for example, ``libclang-release_18.1.5-based-windows-vs2019_64.7z``.
+
+Note that from version 12 onwards, the prebuilt Windows binaries from
+`LLVM <https://www.llvm.org>`_ no longer contain CMake configuration files; so
+they can no longer be used.
+
+Extract the files, and leave it on any desired path, for example, ``c:``,
+and set the environment variable required::
+
+ set LLVM_INSTALL_DIR=c:\libclang
+ set PATH=C:\libclang\bin;%PATH%
+
+Getting PySide
+~~~~~~~~~~~~~~
+
+Cloning the official repository can be done by::
+
+ git clone https://code.qt.io/pyside/pyside-setup
+
+Checking out the version that we want to build, for example, 6.8::
+
+ cd pyside-setup && git checkout 6.8
+
+Install the general dependencies::
+
+ pip install -r requirements.txt
+
+For building the documentation::
+
+ pip install -r requirements-doc.txt
+
+.. note:: Keep in mind you need to use the same version as your Qt installation
+
+.. note:: With `uv`_, use `uv pip install ...`
+
+Building PySide
+~~~~~~~~~~~~~~~
+
+Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
+for example, ``C:\Qt\6.8.0\msvc2019_64\bin\qtpaths.exe``.
+
+Build can take a few minutes, so it is recommended to use more than one CPU core::
+
+ python setup.py build --qtpaths=c:\path\to\qtpaths.exe --openssl=c:\path\to\openssl\bin --build-tests --ignore-git --parallel=8
+
+With `uv`_, this command becomes::
+
+ uv run setup.py build --qtpaths=c:\path\to\qtpaths.exe --openssl=c:\path\to\openssl\bin --build-tests --ignore-git --parallel=8
+
+
+.. _creating_windows_debug_builds:
+
+Creating Debug Builds
+~~~~~~~~~~~~~~~~~~~~~
+
+* Choose *Custom Installation* when installing Python and tick the options for
+ debug binaries and libraries
+
+* Use ``venv`` to create a virtual environment and pass the debug binary::
+
+ python_d.exe -m venv testenv_d
+
+* Use ``python_d.exe`` to invoke ``setup.py``
+
+.. note:: Make sure you add the ``--debug`` option to the ``python setup.py install`` to produce a debug build
+
+
+Installing PySide
+~~~~~~~~~~~~~~~~~
+
+First, create the wheels using the `create_wheels.py`_ script::
+
+ python create_wheels.py --build-dir=C:\directory\where\pyside\is\built --no-examples
+
+On successful completion, the wheels will be created in the `dist` directory.
+
+.. note:: The `build-dir` typically looks like `build\<your_python_environment_name>`. The
+ requirement is that this `build-dir` should contain the `packages_for_wheel` directory.
+ If the `python setup.py` build command was successful, this directory should be present.
+
+Finally, to install the wheels, use the following command::
+
+ pip install dist\*.whl
+
+
+Test installation
+~~~~~~~~~~~~~~~~~
+
+You can execute one of the examples to verify the process is properly working.
+Remember to properly set the environment variables for Qt and PySide::
+
+ python examples\widgets\widgets\tetrix\tetrix.py
+
+.. _`uv`: https://docs.astral.sh/uv/
`qtpip`_ is a wrapper around `pip`_ (the package installer for Python) that provides an
integration with the detection of commercial licenses.
+It requires that a Qt license is present. On a fresh install,
+this can be done by launching the `Qt Maintenance Tool`_ first.
+
To set up the tool, we recommend creating a virtual environment for your
project, and then installing the tool like any other module:
Complementary to the wheels, you will be able to download the sources
as well.
-.. note:: Wheels installed this way will be detectable by `*Qt Creator*`_, which
+.. note:: Wheels installed this way will be detectable by `Qt Creator`_, which
will offer you to install them for your current Python interpreter.
Using account.qt.io
packages contain, please check the :ref:`package_details` page.
Once you download the packages, you are encouraged to create a Python virtual
-environment to install them - check the ref:`quick-start` page for how to do
+environment to install them - check the :ref:`getting-started` page for how to do
it.
With your activated environment on a terminal, run the following command (for
macOS/Linux)::
.. _`Qt Creator`: https://www.qt.io/product/development-tools
.. _`qtpip`: https://pypi.org/project/qtpip/
.. _`pip`: https://pypi.org/project/pip/
-
import sys
import os
+from docutils import nodes
+from docutils.parsers.rst import roles
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
'sphinx.ext.graphviz', 'inheritance_diagram', 'pysideinclude',
'sphinx.ext.viewcode',
'sphinx_design', 'sphinx_copybutton', 'myst_parser', 'sphinx_tags',
- 'sphinx_toolbox.decorators']
+ 'sphinx_toolbox.decorators', 'sphinx_reredirects']
myst_enable_extensions = [
"amsmath",
output_format='@DOC_OUTPUT_FORMAT@'
+def deco_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
+ # Create a reference node
+ node = nodes.reference(rawtext, text, refuri='#', **options)
+ return [node], []
+
def setup(app):
- app.add_config_value('output_format','qthelp','env')
+ app.add_config_value('output_format', 'qthelp', 'env')
+ roles.register_local_role('deco', deco_role)
rst_epilog = """
.. |project| replace:: Qt for Python
"color-admonition-title--important": "#2cde85",
"color-admonition-title-background--important": "#474b53",
"font-stack": "'Titillium Web', sans-serif",
+ "color-background": "rgba(0, 0, 0, 0.9)",
+ "color-background-font": "#FFFFFF",
},
"light_css_variables": {
"color-brand-primary": "#27138b",
"color-brand-content": "#27138b",
"color-admonition-title--important": "#27138b",
"font-stack": "'Titillium Web', sans-serif",
+ "color-background": "rgba(255, 255, 255, 0.9)",
+ "color-background-font": "#000000",
},
}
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
-html_favicon = "_static/qtforpython.ico"
+html_favicon = "@CMAKE_CURRENT_SOURCE_DIR@/_static/qtforpython.ico"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
tags_badge_colors = {
"Android": "info",
}
+
+# link redirection in sphinx
+redirects = {
+ "gettingstarted/porting_from2.html": "../faq/porting_from2.html",
+ "gettingstarted/package_details.html": "../package_details.html",
+ "gettingstarted/index.html": "../building_from_source/index.html",
+ "gettingstarted/linux.html": "../building_from_source/linux.html",
+ "gettingstarted/windows.html": "../building_from_source/windows.html",
+ "gettingstarted/macOS.html": "../building_from_source/macOS.html",
+ "quickstart.html": "gettingstarted.html"
+}
+
+# ignore relative paths when linkcheck is running
+# sphinx-build -b linkcheck -j auto -n -c <folder_of_conf.py> <folder_of_rst> <output_dir>
+linkcheck_ignore = [
+ r'^\.\./', # Ignore all paths starting with ../
+ r'^\./', # Ignore all paths starting with ./
+]
self.table.horizontal_header().section_resize_mode = QHeaderView.Stretch
Additionally, properties can also be declared directly in Shiboken for
-non Qt-libraries, see :ref:`property-declare`.
+non Qt-libraries, see :ref:`property-declare <shiboken6:property-declare>`.
More about features
~~~~~~~~~~~~~~~~~~~
* ``pyside6-rcc``: Qt Resource Compiler. This is a command line tool
that compiles ``.qrc`` files containing binary data, for example images,
- into executable Python code (see :ref:`using_qrc_files`).
+ into executable Python code (see :ref:`tutorial_qrcfiles`).
* ``pyside6-uic``: Qt User Interface Compiler. This is a command line tool
that compiles ``.ui`` files containing designs of Qt Widget-based forms
- into executable Python code (see :ref:`using_ui_files`).
+ into executable Python code (see :ref:`tutorial_uifiles`).
* ``pyside6-assistant``: Qt Help Viewer. This is a graphical tool that can
be used to view Qt documentation from Qt Compressed Help files (``.qhc``).
Currently, only the binary without documentation sets is shipped to reduce the
wheel size. For building the documentation, see :ref:`building_documentation`.
* ``pyside6-designer``: Qt User Interface Designer. This is a graphical tool
to create designs of Qt Widget-based forms and use custom widgets
- (see :ref:`using_ui_files`, :ref:`designer_custom_widgets`).
+ (see :ref:`tutorial_uifiles`, :ref:`designer_custom_widgets`).
.. _NewEnumSystem:
When a PySide6 application that uses the permission API is run in interpreted mode, i.e.,
``python <main_file>.py``, the code implementing the permission API *will not work*. The only way
to make your PySide6 application using permission API work is to bundle the application. For Android,
-this means using the `pyside6-android-deploy`_ tool and for macOS, this means using the
-`pyside6-deploy`_ tool.
+this means using the :ref:`pyside6-android-deploy` tool and for macOS, this means using the
+:ref:`pyside6-deploy` tool.
When running in interpreted mode, you can skip over the permission check/request using the following
*if* condition
Android
~~~~~~~~
-For Android, `pyside6-android-deploy`_ takes care of identifying the necessary permissions needed by
+For Android, :ref:`pyside6-android-deploy` takes care of identifying the necessary permissions needed by
the application and adding those permissions to the *AndroidManifest.xml* using the
*<uses-permission>* element.
*Info.plist* of the Python framework installation to make the Qt permission API work when running
a PySide6 application from the terminal. However, this is not recommended. Therefore, the only
viable solution is to bundle the PySide6 application as a macOS application bundle using
-`pyside6-deploy`_. This macOS application bundle will have its own Info.plist file.
+:ref:`pyside6-deploy`. This macOS application bundle will have its own Info.plist file.
.. _`Blog post`: https://www.qt.io/blog/permission-apis-in-qt-6.5
.. _`Camera Example`: https://doc.qt.io/qtforpython-6/examples/example_multimedia_camera.html#camera-example
-.. _`pyside6-android-deploy`: https://doc.qt.io/qtforpython-6/gettingstarted/package_details.html#deployment
-.. _`pyside6-deploy`: https://doc.qt.io/qtforpython-6/gettingstarted/package_details.html#deployment
.. toctree::
:maxdepth: 2
- quickstart.rst
+ gettingstarted.rst
commercial/index.rst
- gettingstarted/index.rst
+ building_from_source/index.rst
+ package_details.rst
api.rst
tools/index.rst
tutorials/index.rst
deployment/index.rst
considerations.rst
developer/index.rst
+ release_notes/index.rst
..
Intersphinx references in toctrees is not supported
============
* :ref:`modindex`
-
This step creates a ``build/`` directory with the following structure::
build
- └── exe.linux-x86_64-3.7
+ └── exe.linux-x86_64-3.12
└── lib
└── main
The first directory inside ``build/`` depends on the platform
-you are using, in this case a ``x86_64`` Linux using Python 3.7.
+you are using, in this case a ``x86_64`` Linux using Python 3.12.
The structure is the same as previously described, and you can simply
enter the directory and execute the file::
- cd build/exe.linux-x86_64-3.7
+ cd build/exe.linux-x86_64-3.12
./main
This process takes a bit longer, but in the end you have one executable ``hello.bin``::
./hello.bin
-
-
-Some Caveats
-============
-
-
-Nuitka issue on macOS
----------------------
-
-Nuitka currently has a problem with the macOS bundle files on current macOS versions.
-That has the effect that ``--standalone`` and ``--onefile`` create a crashing application.
-Older versions which don't have the recent macOS API changes from 2020 will work.
-We are currently trying to fix that problem.
determines whether a `.apk` or a `.aab` is created.
.. warning:: Currently, users are required to cross-compile Qt for Python to generate the wheels
- required for a specific Android target architecture. This requirement will disappear when
- there are official Qt for Python Android wheels (*in progress*). Because of this
- requirement ``pyside6-android-deploy`` will be considered in **Technical Preview**.
- Instructions on cross-compiling Qt for Python for Android can be found
- :ref:`here <cross_compile_android>`.
+ required for `armeabi-v7a` and `x86` Andorid platforms. Instructions on cross-compiling
+ Qt for Python for Android can be found :ref:`here <cross_compile_android>`.
-.. note:: ``pyside6-android-deploy`` only works on a Linux host at the moment. This constraint
- is also because Qt for Python cross-compilation for Android currently only works on Linux
- systems.
+.. note:: ``pyside6-android-deploy`` only works with a Unix (Linux or macOS) host at the moment.
+
+Prerequisites
+=============
+
+Before using ``pyside6-android-deploy``, ensure that the following prerequisites are met:
+
+.. _android_prerequisites:
+
+Download Android NDK and SDK
+----------------------------
+
+The NDK required corresponds to the NDK version required by the Qt version you are using. See
+`Qt for Android <https://doc.qt.io/qt-6/android.html>`_ for more information.
+
+The easiest way to download the Android NDK is through a script located in the Qt for Python
+repository. To run the script, follow these steps:
+
+#. Clone the Qt for Python repository:
+
+.. code-block:: bash
+
+ git clone https://code.qt.io/pyside/pyside-setup
+
+#. Run the script:
+
+.. code-block:: bash
+
+ cd pyside-setup
+ python tools/cross_compile_android/main.py --download-only --skip-update --auto-accept-license
+
+The script will download the Android NDK and SDK packages required into your home
+directory as a directory called ``.pyside6-android-deploy``. ``pyside6-android-deploy`` will
+automatically detect the NDK and SDK from this cache directory.
+
+If you want to try to download the NDK and SDK manually, you can do so from the following steps
+(for Qt 6.8):
+
+1. Download the sdkmanager using the instructions provided in the `Android Studio
+ documentation <https://developer.android.com/studio/command-line/sdkmanager>`_.
+
+2. Using the sdkmanager download the following packages (for Qt 6.8)::
+
+ "platform-tools", "platforms;android-34", "build-tools;35.0.0"
+
+ and install the NDK using the following command (for Qt 6.8)::
+
+ "ndk;26.1.10909125"
+
+.. note:: The NDK version and the SDK packages required corresponds to the requirements from the
+ Qt version you are using. See `Qt for Android <https://doc.qt.io/qt-6/android.html>`_ for more
+ information.
+
+Download the Qt for Python Android wheels
+-----------------------------------------
+
+There are two ways to download the Qt for Python Android wheels:
+
+1. Download the wheels from the `Qt for Python downloads page`_.
+
+2. Use :ref:`qtpip` download the wheels with the following command:
+
+.. code-block:: bash
+
+ qtpip download PySide6 --android --arch aarch64
+
+for the `aarch64` architecture. The available architectures are `aarch64` and `x86_64`.
How to use it?
==============
-Like ``pyside6-deploy``, there are :ref:`two different ways <how_pysidedeploy>` with which
+Like :ref:`pyside6-deploy`, there are :ref:`two different ways <how_pysidedeploy>` with which
you can deploy your PySide6 application using ``pyside6-android-deploy``. The only difference is
that for ``pyside6-android-deploy`` to work, the main Python entry point file should be named
``main.py``.
+To deploy the application, run the following command:
+
+.. code-block:: bash
+
+ pyside6-android-deploy --name "MyApp"
+ --wheel-pyside=path_to_downloaded_PySide_wheel
+ --wheel-shiboken=path_to_downloaded_Shiboken_wheel
+ --ndk-path=path_to_ndk
+ --sdk-path=path_to_sdk
+
+The ``--ndk-path`` and ``--sdk-path`` options are optional if you used the script provided in the
+:ref:`android_prerequisites` section to download the NDK and SDK.
+
+For any subsequent deployments, you can use the ``pysidedeploy.spec`` file to control the various
+parameters of the deployment process. The command to deploy the application using the
+``pysidedeploy.spec`` file is:
+
+.. code-block:: bash
+
+ pyside6-android-deploy --config-file path_to_pysidedeploy.spec
+
+The `config-file` option is optional if you are running the command from the project directory
+where ``pysidedeploy.spec`` is located.
+
.. _pysideandroiddeploy:
pysidedeploy.spec
Currently, cross-compiling Qt for Python wheels only works with a Linux host. Follow these steps
to cross-compile Qt for Python Android wheels.
-#. `Download <qt_download>`_ and install Qt version for which you would like to create Qt for Python
+#. `Download <qt_download_>`_ and install Qt version for which you would like to create Qt for Python
wheels.
#. Cloning the Qt for Python repository::
.. _`python-for-android`: https://python-for-android.readthedocs.io/en/latest/
.. _`qt_download`: https://www.qt.io/download
.. _`cpython`: https://pypi.org/project/Cython/
+.. _`Qt for Python downloads page`: https://download.qt.io/official_releases/QtForPython/pyside6/
The final executable produced has a ``.exe`` suffix on Windows, ``.bin`` on Linux and ``.app`` on
macOS.
-.. note:: Although using a virtual environment for Python is recommended for ``pyside6-deploy``, do
- not add the virtual environment to the application directory you are trying to deploy.
- ``pyside6-deploy`` will try to package this venv folder and will eventually fail.
-
-.. note:: The default version of Nuitka used with the tool is version ``2.3.2``. This can be
+.. note:: The default version of Nuitka used with the tool is version ``2.5.1``. This can be
updated to a newer version by updating your ``pysidedeploy.spec`` file.
.. _how_pysidedeploy:
pysidedeploy.spec
=================
-As mentioned in the `Approach 2 <approach_two>`_ above, you can use this file to control the various
+As mentioned in the :ref:`Approach 2 <approach_two>` above, you can use this file to control the various
parameters of the deployment process. The file has multiple sections, with each section containing
multiple keys (parameters being controlled) assigned to a value. The advantages of such a file are
two folds:
NSCameraUsageDescription:CameraAccess
+ * ``mode``: Accepts one of the options: ``onefile`` or ``standalone``. The default is ``onefile``.
+ This option corresponds to the mode in which Nuitka is run. The onefile mode creates a single
+ executable file, while the standalone mode creates a directory with the executable and all the
+ necessary files. The standalone mode is useful when you want to distribute the application as a
+ directory with dependencies and other files required by the app.
+
* ``extra_args``: Any extra Nuitka arguments specified. It is specified as space-separated
command line arguments i.e. just like how you would specify it when you use Nuitka through
the command line. By default, it contains the following arguments::
* - macOS
- dyld_info
- Available by default from macOS 12 and upwards
+
+Creating a bug report
+=====================
+
+If you are unsure if the bug is from ``pyside6-deploy`` or ``Nuitka``:
+
+#. Create a bug report in Qt for Python. See instructions
+ `here <https://wiki.qt.io/Qt_for_Python/Reporting_Bugs>`_.
+
+#. Run ``pyside6-deploy`` command with the ``--verbose`` option and replace ``--quiet`` with
+ ``--verbose`` in the ``extra_args`` parameter in the ``pysidedeploy.spec`` file. Attach the
+ output from stdout to the bug report.
+
+#. Attach a minimal example that reproduces the bug with the bug report.
+
+If you think the bug originates from ``Nuitka``:
+
+#. Try using a newer version of ``Nuitka``. You can change this from the ``packages`` parameter in
+ your generated ``pysidedeploy.spec`` file.
+
+#. If the bug persists, create a bug report on the
+ `Nuitka GitHub page <https://github.com/Nuitka/Nuitka/issues>`_.
+
+ * Run ``pyside6-deploy`` with the ``--dry-run`` option to see the actual ``Nuitka`` command
+ generated. Attach the ``Nuitka`` command ran to the bug report.
+ * Follow the Nuitka bug report template to create a bug report.
- Add entry to ``sources/pyside6/doc/modules.rst``.
- Add a .qdocconf.in file in ``sources/pyside6/doc/qtmodules``.
-- Add module description ``.rst`` file in ``sources/pyside6/doc/extras``.
+- Add module description ``.rst`` file in ``sources/pyside6/doc/extras``:
+ * Run a full documentation build
+ * Copy the auto-generated file ``<build>/pyside6/doc/base/<Module>/index.rst``
+ to ``sources/pyside6/doc/extras/<Module>/<Module>.rst``
+ * Remove TOC, qmake/CMake instructions and class lists (cf existing files)
+ * Check the references, preferably link to converted documents
+ in PySide or add documents there (see :ref:`maintaining-additionaldocs`).
in the ``doc`` directory containing some description explaining them
(see the `samplebinding` example).
+.. note:: The ``example_gallery`` tools automatically prepends an
+ auto-generated anchor to the example documentation, please use those for
+ linking to the documentation.
+
Port a Qt example
-----------------
- Add the tool in ``sources/pyside-tools/pyside_tool.py``.
- Add the tool in ``build_scripts/__init__.py`` to create the setuptools entry points
i.e. this enable using the tool from the console as "pyside6-<tool_name>"
-- Add an entry to ``sources/pyside6/doc/gettingstarted/package_details.rst``.
+- Add an entry to ``sources/pyside6/doc/package_details.rst``.
- Include the necessary Qt binaries explicitly on ``build_scripts/wheel_files.py``
- Build with ``--standalone``, verify it is working.
The descriptions may link to tutorials which can be added to additionaldocs.lst
for webxml extraction.
+.. _maintaining-additionaldocs:
+
Maintaining additionaldocs.lst
------------------------------
The file is a list of additional documentation files. These are basically Qt
-tutorials referenced by the documentation. They will receive some Python
-adaption by shiboken/sphinx.
+tutorials referenced by the documentation, for example "Additional Frameworks"
+sections of module files. They will receive some Python adaption by
+shiboken/sphinx.
+
+A line in brackets denotes the output directory.
The list can be created by the below script and some hand-editing. It will find
almost all documents. Quite a number of them might be unreferenced, but there
for F in *.webxml
do
- echo "$F" | egrep '(-index)|(-module)|(-qmlmodule)\.webxml$' > /dev/null
+ echo "$F" | egrep '(-index|example|cmake|private-module|-changes-qt6|-module.web|-qmlmodule.web)' > /dev/null
if [ $? -ne 0 ]
then
if fgrep '<para>' "$F" > /dev/null # Exclude reference only
then
- egrep "(<class )|(<namespace )" $F > /dev/null || echo $F
+ egrep "(<namespace|<class|<struct|<group|<union)" $F > /dev/null || echo $F
fi
fi
done
+The overviews go into a directory named ``overviews``. There are also special
+pages containing lists of classes with brief, grouped by function. They mostly
+have a ``<group name="...">`` WebXML element, but may also be hand-written.
+They should be referenced from the module page in a section named `List of
+Classes by Function`. For clarity, the files with a ``<group>`` element are
+placed into a separate directory ``groups``.
+
Inheritance graphs
------------------
There are 2 scripts used for determining the inheritance:
* ``json_inheritance.py`` (env var ``INHERITANCE_FILE``) reads a
- inheritance.json file containing the class hierarchy generated by
- shiboken's doc generator.
+inheritance.json file containing the class hierarchy generated by
+shiboken's doc generator.
* ``import_inheritance.py`` actually tries to import the class (legacy)
documentation.rst
adapt_qt.rst
extras.rst
+ qtasyncio.rst
+ signalmanager.md
Implementation details
----------------------
enumfeatures_doc.rst
limited_api.rst
signature_doc.rst
+ mypy-correctness.rst
feature-motivation.rst
--- /dev/null
+.. mypy-correctness:
+
+Improving the Quality of Signatures with mypy
+=============================================
+
+Preliminary
+-----------
+
+The Python Interface files of PySide are generated by a few scripts.
+When ``.pyi`` files were started in 2017, a minimal syntax check was
+possible because these files could be run in ``Python`` itself.
+
+Some changes to the format of ``.pyi`` files made that impossible, leaving
+``PySide``'s ``.pyi`` files quite unchecked for years. Only correct parsing of
+all functions could be checked by the generator.
+
+The introduction of the ``mypy`` tool as a rigorous error checker for the
+generated files brought many improvements, but also some surprizes.
+
+
+Running the mypy Tests
+----------------------
+
+The ``mypy`` tests are automatically run by the Qt company CI framework (COIN).
+When you have ``mypy`` installed, the tests are run when building with tests.
+In debug mode, this can take more than 30 s, therefore we provide the
+translation option
+
+.. code-block:: shell
+
+ --skip-mypy-test
+
+which can be used when repeatedly translating. But note that ``mypy`` has a
+good cache that suppresses analysis of unchanged ``.pyi`` files.
+
+
+Types of mypy Errors
+--------------------
+
+Duplication Errors
+~~~~~~~~~~~~~~~~~~
+
+Many functions have multiple signatures, which are later translated to multiple
+``typing.overload`` versions in the ``.pyi`` file.
+Due to the mapping of ``C++`` functions to ``Python`` it sometimes happens
+that similar ``C++`` functions become ``Python`` duplicates. This was simple
+to filter out, but ``mypy`` still finds duplicates which differ only in parameter
+names. This is now handled by the function ``remove_ambiguous_signatures()``
+in module ``layout`` that compares the so-called ``annotations`` which ignore
+parameter names.
+
+
+Shadowing Errors
+~~~~~~~~~~~~~~~~
+
+A quite subtle error type is the shadowing of multiple signatures. This is due
+to the sequential nature of ``.pyi`` files::
+
+ * In ``C++``, the order of functions does not matter at all. The best fit is
+ automatically used.
+
+ * In Python stub files, the alternatives of multiple signatures are sequentially
+ checked in ``@typing.overload`` chains of functions.
+ This can produce shadowing when an annotation contains another.
+
+An Example: :class:`PySide6.QtCore.QCborSimpleType` is shadowed by int
+when int is listed first. That is due to the Method Resolution Order ``mro()``::
+
+ * int.mro() [<class 'int'>, <class 'object'>]
+
+ * QCborSimpleType.mro() [<enum 'QCborSimpleType'>, <enum 'IntEnum'>,
+ <class 'int'>, <enum 'ReprEnum'>,
+ <enum 'Enum'>, <class 'object'>]
+
+You see that the ``mro()`` has an ordering effect on the multiple signatures.
+The enum inherits from ``int`` and should come before the ``int`` entry.
+The whole task of bringing the multiple signatures into a conflict-free order
+is a sort of ``Topological Sorting``.
+
+We build a sorting key using the length of the ``mro`` of the argument annotations
+and some additional heuristics. They can be inspected in function ``get_ordering_key()``
+that is called by ``sort_by_inheritance()`` in module ``layout``.
+
+
+Unsolvable Errors
+-----------------
+
+Some errors are pointed out by mypy that we cannot solve. The only chance we have is
+to disable these errors partially or even completely. They are marked in the ``.pyi`` files,
+see below.
+
+
+Contradiction to Qt
+~~~~~~~~~~~~~~~~~~~
+
+Errors are found by mypy where Qt has a different opinion. The error types
+"override" and "overload-overlap" needed to be disabled because we cannot
+change what Qt thinks is right.
+
+Examples:
+
+::
+
+ Error code "override" cannot be fixed because the problem
+ is situated in Qt itself:
+
+ Signature of "open" incompatible with supertype "QFile"
+
+ Error code "overload-overlap" also cannot be fixed because
+ we have no chance to modify return-types:
+
+ Overloaded function signatures 1 and 6 overlap with
+ incompatible return types
+
+They are globally disabled by the comment::
+
+ # mypy: disable-error-code="override, overload-overlap"
+
+Other errors like "misc" are too broad to be prematurely disabled.
+See below how we handle them.
+
+
+Disagreement with __add__ and __iadd__
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are internal rules for ``Python`` which can only be recognized when
+``mypy`` points them out as "misc". There are functions which come in pairs:
+
+.. code-block:: python
+
+ __add__, __iadd__, __sub__, __isub__, __mul__, __imul__, ...
+
+and more. There is this rule::
+
+ if __add__ and __iadd__ exist in a type, the signatures must be the same.
+
+In 95 % this rule is fulfilled, but in a few cases it is not. There we have
+to compute these cases, and if they disagree we generate a disabling ``mypy``
+inline comment "# type: ignore[misc]". You can see this functionality in
+``ExactEnumerator.klass`` of module ``enum_sig``.
+
+
+Disagreement with inconsistent overloads
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If there is a mixed overloading of methods and static or class methods, mypy
+believes this is an error. In a first version, we fixed this rare situation
+by suppressing this "misc" error. But when moving to correct positional-only
+parameters (PEP 570) this suppression created unsolvable follow-up errors.
+The cleaner solution was to remove the static methods and prefer the normal methods.
+
+See function ``is_inconsistent_overload()`` of module
+``layout`` which checks if "self" is always or never an argument.
+
+
+Conclusion and Future
+---------------------
+
+This effort has brought the reported ``mypy`` errors from 601 down to zero, which
+is really an improvement. But there can be done more. Although we now know that we
+are generating syntactically and semantically quite correct files, we still do not know
+whether the real types really fulfil the requirements of ``mypy``.
+
+There is a ``stubtest`` module in ``mypy`` which we might perhaps use to do even
+more tests. These would check if the implementation and stub files agree.
+
+
+Literature
+----------
+
+* `mypy error codes <https://mypy.readthedocs.io/en/stable/error_code_list.html>`__
+ We use these by default enabled codes.
+
+* `typing — Support for type hints <https://docs.python.org/3/library/typing.html>`__
+ The Python documentation of the typing module
+
+* `Typing cheat sheet <https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html>`__
+ A quick overview of type hints (hosted at the mypy docs)
+
+* "Type System Reference" section of `the mypy docs <https://mypy.readthedocs.io/en/stable/index.html>`__
+ The Python typing system is standardised via PEPs, so this reference should
+ broadly apply to most Python type checkers. (Some parts may still be specific to mypy.)
+
+* `Static Typing with Python <https://typing.readthedocs.io/en/latest/>`__
+ Type-checker-agnostic documentation written by the community detailing type system features, useful typing related tools and typing best practices.
+
+* `Specification for the Python type system <https://typing.readthedocs.io/en/latest/spec/index.html>`__
+ The complete specification. Quite exhaustive.
--- /dev/null
+.. _developer-qtasyncio:
+
+QtAsyncio developer notes
+=========================
+
+QtAsyncio is a module that provides integration between Qt and Python's
+`asyncio`_ module. It allows you to run `asyncio` event loops in a Qt
+application, and to use Qt APIs from `asyncio` coroutines.
+
+.. _asyncio: https://docs.python.org/3/library/asyncio.html
+
+As a pure Python module that integrates deeply with another Python
+module, developing for QtAsyncio has some interesting aspects that
+differ from other parts of Qt for Python.
+
+QtAsyncio vs asyncio
+--------------------
+
+QtAsyncio has an interesting property from a developer perspective. As
+one of its purposes is to be a transparent replacement to asyncio's own
+event loop, for any given code that uses asyncio APIs, QtAsyncio will
+(or should!) behave identically to asyncio. (Note that the reverse does
+not apply if we also have Qt code). This is especially handy when
+debugging, as one can run the asyncio code to compare and to check the
+expected behavior. It also helps to debug both QtAsyncio and asyncio
+step by step and compare the program flow to find where QtAsyncio might
+go wrong (as far as their code differences allow). Often, one will also
+be able to implement a unit test by asserting that QtAsyncio's output is
+identical to asyncio's. (Note that a limitation to this is the fact that
+Qt's event loop does not guarantee the order of execution of callbacks,
+while asyncio does.)
+
+The step function
+-----------------
+
+When debugging, a significant amount of time will likely be spent inside
+the ``_step`` method of the ``QAsyncioTask`` class, as it is called
+often and it is where coroutines are executed. You will find that it is
+similar to asyncio's equivalent method, but with some differences. Read
+the in-line comments carefully. Some variables and associated lines of
+code might seem innocuous or even unnecessary, but provide important bug
+fixes.
+
+Keeping QtAsyncio in sync with asyncio
+--------------------------------------
+
+QtAsyncio implements `asyncio's API <https://docs.python.org/3/library/asyncio.html>`_,
+which can change with new releases. As it is part of the standard
+library, this lines up with new Python versions. It is therefore good
+practice to check whether the API has changed for every new Python
+version. This can include new classes or methods, changed signatures for
+existing functions, deprecations or outright removals. For example,
+event loop policies are expected to be deprecated with Python 3.13, with
+subsequent removal in Python 3.15. This removal will in turn require a
+small refactoring to make sure the ``QtAsyncio.run()`` function no
+longer uses the policy mechanism.
+
+asyncio developer guidance
+--------------------------
+
+asyncio's resources go beyond the pure API documentation. E.g., asyncio
+provides its own `developer guidance <https://docs.python.org/3/library/asyncio-dev.html>`_
+that is worth following when developing for QtAsyncio. In addition,
+there is documentation for
+`extending asyncio <https://docs.python.org/3/library/asyncio-extending.html>`_
+that details a few essential points to keep in mind when building your
+own event loop.
--- /dev/null
+# Signalmanager
+
+# Description
+
+The class `Signalmanager` in *p/sources/pyside6/libpyside* takes care of routing
+Qt signals to the receivers (files *signalmanager.cpp*, *qobjectconnect.cpp*,
+*pysidesignal.cpp*).
+
+There are several kinds of receivers:
+- Slots of C++ classes
+- Slots of `QObject`-derived classes declared in Python using `@Slot`
+- Functions of `QObject`-derived classes declared in Python. They will
+ be turned into proper slots at runtime using the private Qt class
+ `QMetaObjectBuilder`. This causes a warning to be emitted,
+ visible when activating the logging category `qt.pyside.libpyside`.
+- Methods of non-`QObject`-derived classes
+- Other callables (free functions, lambdas, partially bound functions)
+
+Proper Qt connections where `QObject.disconnect()`, `QObject.sender()` and
+`QObject.connectNotify()` work are desirable also for receivers that are not
+`QObject`s. This is achieved by using a `QObject`-derived class
+`GlobalReceiverV2` for a receiver which has a dynamic slot created by
+`QMetaObjectBuilder` to route connected signals to the Python receiver (based
+on internal class `DynamicSlotDataV2`).
+
+The instances of `GlobalReceiverV2` are stored in a hash on the receiver
+Python objects in `Signalmanager`.
+
+`QMetaObject::connect(QObject*,int,QObject*,int)` is used to make the
+connections based on meta method indexes.
+
+Normally, a reference should be kept on the receiver callable. However, in the
+case of a method of a non-`QObject`-derived class, the connection should be
+automatically severed when the instance is deleted. The callable passed in this
+case (`signal.connect(foo.slot)`) is a partially bound function which has the
+`self` parameter. It is decomposed into the `self` parameter and the method. A
+reference is kept on the method. A weak reference with destruction callback is
+kept for `self`.
+
+## Issues
+
+- [Receiver Leak PYSIDE-1057](https://bugreports.qt.io/browse/PYSIDE-1057)
+- [Partial function receiver Leak PYSIDE-2793](https://bugreports.qt.io/browse/PYSIDE-2793)
+- Various issues related to threading and object deletion, solved by workarounds
+ ([PYSIDE-2646](https://bugreports.qt.io/browse/PYSIDE-2646))
+- Complicated code, hard to maintain
+- Disconnect does not work for `QObject.connect()` with context argument; it also
+ leaks methods
+
+## Plans
+
+Change
+[QObject: Add connect() overload with context arg acab25a3ccb836818e5089b23d40196bc7414b7a](https://codereview.qt-project.org/c/pyside/pyside-setup/+/536298)
+implements a connection based on `QtPrivate::QSlotObjectBase` and
+`QObjectPrivate::connect(QObject*,int,QObject*,QtPrivate::QSlotObjectBase*)`
+(*pysideqslotobject.cpp*). This could in principle enable removing
+`GlobalReceiverV2`, but requires keeping the `QMetaObject::Connection` for
+disconnecting.
The signature C extension
=========================
-This module is a C extension for CPython 3.5 and up, and CPython 2.7.
+This module is a C extension for CPython 3.X.
Its purpose is to provide support for the ``__signature__`` attribute
of builtin PyCFunction objects.
shiboken6/libshiboken/signature.cpp . All other functionality is implemented in
the ``signature`` Python package. It has the following structure::
- sources/shiboken6/shibokenmodule/files.dir/shibokensupport
- ├── __init__.py
- ├── feature.py
- ├── fix-complaints.py
- ├── shibokensupport.pyproject
- └── signature
- ├── PSF-3.7.0.txt
- ├── __init__.py
- ├── errorhandler.py
- ├── importhandler.py
- ├── layout.py
- ├── lib
- │  ├── __init__.py
- │  ├── enum_sig.py
- │  ├── pyi_generator.py
- │  └── tool.py
- ├── loader.py
- ├── mapping.py
- ├── parser.py
- └── qt_attribution.json
+ sources/shiboken6/shibokenmodule/files.dir/shibokensupport
+ ├── __init__.py
+ ├── feature.py
+ ├── fix-complaints.py
+ ├── shibokensupport.pyproject
+ └── signature
+ ├── PSF-3.7.0.txt
+ ├── __init__.py
+ ├── errorhandler.py
+ ├── importhandler.py
+ ├── layout.py
+ ├── lib
+ │ ├── __init__.py
+ │ ├── enum_sig.py
+ │ ├── pyi_generator.py
+ │ └── tool.py
+ ├── loader.py
+ ├── mapping.py
+ ├── parser.py
+ └── qt_attribution.json
Really important are the **parser**, **mapping**, **errorhandler**, **enum_sig**,
**layout** and **loader** modules. The rest is needed to create Python 2 compatibility
This was implemented in ``Qt For Python 5.12.1``.
+Update and Future of the Signature Module
+-----------------------------------------
+
+.. code-block:: bash
+
+ PYSIDE-2101: The __signature__ attribute is gone due to rlcompleter.
+
+End of 2022, a change to the rlcompleter module made it impossible to further
+support an unofficial ``__signature__`` attribute in PySide. From then on,
+the functionality of signatures was kept by a ``get_signature`` function.
+
+Over the years, the requirements for the correctness of the generated pyi files
+have increased drastically, and a lot of effort went into making the generated
+``.pyi`` files correct for the current ``mypy`` tool. Mode information
+about the kind of errors corrected can be found in the :ref:`mypy-correctnes` section.
+
+
Literature
----------
from PySide6 import QtCharts
-The module also provides `QML types <https://doc.qt.io/qt-6/qtcharts-qmlmodule.html>`_ .
-
.. note:: An instance of QApplication is required for the QML types as the
module depends on Qt's \l{Graphics View Framework} for rendering.
QGuiApplication is not sufficient.
+PySide6.QtCore.ClassInfo
+========================
+
.. currentmodule:: PySide6.QtCore
.. py:decorator:: ClassInfo
-This decorator is used to associate extra information to the class, which is available
-using ``QObject.metaObject()``. This information is used by the
-*Qt D-Bus* and *Qt Qml* modules.
-
-The extra information takes the form of a dictionary with key and value in a literal string.
+ This decorator is used to associate extra information to the class, which is available
+ using :meth:`QObject.metaObject`. This information is used by the
+ *Qt D-Bus* and *Qt Qml* modules.
-The recommended usage is to provide the key/value using python keyword syntax, where the
-keyword becomes the key, and the provided string becomes the value.
+ The extra information takes the form of a dictionary with key and value in a literal string.
-If the key needs to contain special characters (spaces, commas, '::', start with a number, etc),
-it is also possible to pass a python dictionary with arbitrary strings for both the key and
-value and enabling special characters in the key.
+ The recommended usage is to provide the key/value using python keyword syntax, where the
+ keyword becomes the key, and the provided string becomes the value.
-.. note:: This decorator is a implementation of the Q_CLASSINFO macro.
+ If the key needs to contain special characters (spaces, commas, '::', start with a number, etc),
+ it is also possible to pass a python dictionary with arbitrary strings for both the key and
+ value and enabling special characters in the key.
+ .. note:: This decorator is a implementation of the ``Q_CLASSINFO`` macro.
-Example
--------
+ Example
+ -------
-.. code-block:: python
+ .. code-block:: python
- # Recommended syntax
- @ClassInfo(Author='PySide Team', URL='http://www.pyside.org')
- class MyObject(QObject):
- ...
+ # Recommended syntax
+ @ClassInfo(Author='PySide Team', URL='http://www.pyside.org')
+ class MyObject(QObject):
+ ...
- # Provided to support keys not supported by Python's keyword syntax
- @ClassInfo({'Some key text $': 'This syntax supports special characters in keys'})
- class MyObject(QObject):
- ...
+ # Provided to support keys not supported by Python's keyword syntax
+ @ClassInfo({'Some key text $': 'This syntax supports special characters in keys'})
+ class MyObject(QObject):
+ ...
+PySide6.QtCore.Property
+=======================
+
.. currentmodule:: PySide6.QtCore
.. py:class:: Property
-Detailed Description
---------------------
+ Detailed Description
+ --------------------
+
+ The Property function lets you declare properties that
+ behave both as Qt and Python properties, and have their
+ getters and setters defined as Python functions.
+
+ They are equivalent to the ``Q_PROPERTY`` macro in the `Qt Docs`_.
-The Property function lets you declare properties that
-behave both as Qt and Python properties, and have their
-getters and setters defined as Python functions.
+ Here is an example that illustrates how to use this
+ function:
-They are equivalent to the ``Q_PROPERTY`` macro in the `Qt Docs`_.
+ .. code-block:: python
+ :linenos:
-Here is an example that illustrates how to use this
-function:
+ from PySide6.QtCore import QObject, Property
-.. code-block:: python
- :linenos:
+ class MyObject(QObject):
+ def __init__(self, startval=42):
+ QObject.__init__(self)
+ self.ppval = startval
- from PySide6.QtCore import QObject, Property
+ def readPP(self):
+ return self.ppval
- class MyObject(QObject):
- def __init__(self, startval=42):
- QObject.__init__(self)
- self.ppval = startval
+ def setPP(self, val):
+ self.ppval = val
- def readPP(self):
- return self.ppval
+ pp = Property(int, readPP, setPP)
- def setPP(self, val):
- self.ppval = val
+ obj = MyObject()
+ obj.pp = 47
+ print(obj.pp)
- pp = Property(int, readPP, setPP)
+ The full options for ``QtCore.Property`` can be found with ``QtCore.Property.__doc__``:
- obj = MyObject()
- obj.pp = 47
- print(obj.pp)
+ .. code-block:: python
-The full options for ``QtCore.Property`` can be found with ``QtCore.Property.__doc__``:
+ Property(self, type: type,
+ fget: Optional[Callable] = None,
+ fset: Optional[Callable] = None,
+ freset: Optional[Callable] = None,
+ fdel: Optional[Callable] = None,
+ doc: str = '',
+ notify: Optional[Callable] = None,
+ designable: bool = True,
+ scriptable: bool = True,
+ stored: bool = True, user: bool = False,
+ constant: bool = False,
+ final: bool = False) -> PySide6.QtCore.Property
-.. code-block:: python
+ Normally, only ``type``, ``fget``and ``fset`` are used.
- Property(self, type: type,
- fget: Optional[Callable] = None,
- fset: Optional[Callable] = None,
- freset: Optional[Callable] = None,
- fdel: Optional[Callable] = None,
- doc: str = '',
- notify: Optional[Callable] = None,
- designable: bool = True,
- scriptable: bool = True,
- stored: bool = True, user: bool = False,
- constant: bool = False,
- final: bool = False) -> PySide6.QtCore.Property
-Normally, only ``type``, ``fget``and ``fset`` are used.
+ Properties compared with Python properties
+ ------------------------------------------
+ ``Python`` has property objects very similar to ``QtCore.Property``.
+ Despite the fact that the latter has an extra ``freset`` function, the usage
+ of properties is almost the same. The main difference is that ``QtCore.Property``
+ requires a ``type`` parameter.
-Properties compared with Python properties
-------------------------------------------
-``Python`` has property objects very similar to ``QtCore.Property``.
-Despite the fact that the latter has an extra ``freset`` function, the usage
-of properties is almost the same. The main difference is that ``QtCore.Property``
-requires a ``type`` parameter.
+ .. note:: ``Python`` property objects do *not* work in QML; ``QtCore.Property``
+ needs to be used.
-In the above example, the following lines would be equivalent properties:
+ In the above example, the following lines would be equivalent properties:
-.. code-block:: python
+ .. code-block:: python
- pp = QtCore.Property(int, readPP, setPP) # PySide version
- pp = property(readPP, setPP) # Python version
+ pp = QtCore.Property(int, readPP, setPP) # PySide version
+ pp = property(readPP, setPP) # Python version
-As you know from the `Python Docs`_, ``Python`` allows to break the property
-creation into multiple steps, using the decorator syntax. We can do this in
-``PySide`` as well:
+ As you know from the `Python Docs`_, ``Python`` allows to break the property
+ creation into multiple steps, using the decorator syntax. We can do this in
+ ``PySide`` as well:
-.. code-block:: python
- :linenos:
+ .. code-block:: python
+ :linenos:
- from PySide6.QtCore import QObject, Property
+ from PySide6.QtCore import QObject, Property
- class MyObject(QObject):
- def __init__(self, startval=42):
- QObject.__init__(self)
- self.ppval = startval
+ class MyObject(QObject):
+ def __init__(self, startval=42):
+ QObject.__init__(self)
+ self.ppval = startval
- @Property(int)
- def pp(self):
- return self.ppval
+ @Property(int)
+ def pp(self):
+ return self.ppval
- @pp.setter
- def pp(self, val):
- self.ppval = val
+ @pp.setter
+ def pp(self, val):
+ self.ppval = val
- obj = MyObject()
- obj.pp = 47
- print(obj.pp)
+ obj = MyObject()
+ obj.pp = 47
+ print(obj.pp)
-Please be careful here: The two ``Python`` functions have the same name, intentionally.
-This is needed to let ``Python`` know that these functions belong to the same property.
+ Please be careful here: The two ``Python`` functions have the same name, intentionally.
+ This is needed to let ``Python`` know that these functions belong to the same property.
-Properties in QML expressions
------------------------------
+ Properties in QML expressions
+ -----------------------------
-If you are using properties of your objects in QML expressions,
-QML requires that the property changes are notified. Here is an
-example illustrating how to do this:
+ If you are using properties of your objects in QML expressions,
+ QML requires that the property changes are notified. Here is an
+ example illustrating how to do this:
-.. code-block:: python
- :linenos:
+ .. code-block:: python
+ :linenos:
- from PySide6.QtCore import QObject, Signal, Property
+ from PySide6.QtCore import QObject, Signal, Property
- class Person(QObject):
+ class Person(QObject):
- name_changed = Signal()
+ name_changed = Signal()
- def __init__(self, name):
- QObject.__init__(self)
- self._person_name = name
+ def __init__(self, name):
+ QObject.__init__(self)
+ self._person_name = name
- def _name(self):
- return self._person_name
+ def _name(self):
+ return self._person_name
- name = Property(str, _name, notify=name_changed)
+ name = Property(str, _name, notify=name_changed)
.. _`Python Docs`: https://docs.python.org/3/library/functions.html?highlight=property#property
.. _`Qt Docs`: https://doc.qt.io/qt-6/properties.html
+PySide6.QtCore.QEnum
+====================
+
.. currentmodule:: PySide6.QtCore
.. py:decorator:: QEnum
-This class decorator is equivalent to the `Q_ENUM` macro from Qt. The decorator
-is used to register a Python Enum derived class to the meta-object system,
-which is available via `QObject.staticMetaObject`. The enumerator must be in a
-QObject derived class to be registered.
+ This class decorator is equivalent to the `Q_ENUM` macro from Qt. The decorator
+ is used to register a Python Enum derived class to the meta-object system,
+ which is available via `QObject.staticMetaObject`. The enumerator must
+ be in a :class:`QObject` derived class to be registered.
-Example
--------
+ Example
+ -------
-::
+ ::
- from enum import Enum, auto
+ from enum import Enum, auto
- from PySide6.QtCore import QEnum, QObject
+ from PySide6.QtCore import QEnum, QObject
- class Demo(QObject):
+ class Demo(QObject):
- @QEnum
- class Orientation(Enum):
- North, East, South, West = range(4)
+ @QEnum
+ class Orientation(Enum):
+ North, East, South, West = range(4)
-See :deco:`QFlag` for registering Python Flag derived classes.
+ See :deco:`QFlag` for registering Python Flag derived classes.
-Meanwhile all enums and flags have been converted to Python Enums
-(default since ``PySide 6.4``), see the :ref:`NewEnumSystem` section.
+ Meanwhile all enums and flags have been converted to Python Enums
+ (default since ``PySide 6.4``), see the :ref:`NewEnumSystem` section.
+PySide6.QtCore.QFlag
+====================
+
.. currentmodule:: PySide6.QtCore
.. py:decorator:: QFlag
-QFlag handles a variation of the Python Enum, the Flag class.
+ QFlag handles a variation of the Python Enum, the Flag class.
-Please do not confuse that with the Qt QFlags concept. Python does
-not use that concept, it has its own class hierarchy, instead.
-For more details, see the `Python enum documentation <https://docs.python.org/3/library/enum.html>`_.
+ Please do not confuse that with the Qt QFlags concept. Python does
+ not use that concept, it has its own class hierarchy, instead.
+ For more details, see the `Python enum documentation <https://docs.python.org/3/library/enum.html>`_.
-Example
--------
+ Example
+ -------
-::
+ ::
- from enum import Flag, auto
+ from enum import Flag, auto
- from PySide6.QtCore import QFlag, QObject
+ from PySide6.QtCore import QFlag, QObject
- class Demo(QObject):
+ class Demo(QObject):
- @QFlag
- class Color(Flag):
- RED = auto()
- BLUE = auto()
- GREEN = auto()
- WHITE = RED | BLUE | GREEN
+ @QFlag
+ class Color(Flag):
+ RED = auto()
+ BLUE = auto()
+ GREEN = auto()
+ WHITE = RED | BLUE | GREEN
-Details about Qt Flags:
------------------------
+ Details about Qt Flags:
+ -----------------------
-There are some small differences between Qt flags and Python flags.
-In Qt, we have for instance these declarations:
+ There are some small differences between Qt flags and Python flags.
+ In Qt, we have for instance these declarations:
-::
+ ::
- enum QtGui::RenderHint { Antialiasing, TextAntialiasing, SmoothPixmapTransform,
- HighQualityAntialiasing, NonCosmeticDefaultPen }
- flags QtGui::RenderHints
+ enum QtGui::RenderHint { Antialiasing, TextAntialiasing, SmoothPixmapTransform,
+ HighQualityAntialiasing, NonCosmeticDefaultPen }
+ flags QtGui::RenderHints
-The equivalent Python notation would look like this:
+ The equivalent Python notation would look like this:
-::
+ ::
- @QFlag
- class RenderHints(enum.Flag)
- Antialiasing = auto()
- TextAntialiasing = auto()
- SmoothPixmapTransform = auto()
- HighQualityAntialiasing = auto()
- NonCosmeticDefaultPen = auto()
+ @QFlag
+ class RenderHints(enum.Flag)
+ Antialiasing = auto()
+ TextAntialiasing = auto()
+ SmoothPixmapTransform = auto()
+ HighQualityAntialiasing = auto()
+ NonCosmeticDefaultPen = auto()
-As another example, the Qt::AlignmentFlag flag has 'AlignmentFlag' as the enum
-name, but 'Alignment' as the type name. Non flag enums have the same type and
-enum names.
+ As another example, the Qt::AlignmentFlag flag has 'AlignmentFlag' as the enum
+ name, but 'Alignment' as the type name. Non flag enums have the same type and
+ enum names.
-::
+ ::
- enum Qt::AlignmentFlag
- flags Qt::Alignment
+ enum Qt::AlignmentFlag
+ flags Qt::Alignment
-The Python way to specify this would be
+ The Python way to specify this would be
-::
+ ::
- @QFlag
- class Alignment(enum.Flag):
- ...
+ @QFlag
+ class Alignment(enum.Flag):
+ ...
-See :deco:`QEnum` for registering Python Enum derived classes.
+ See :deco:`QEnum` for registering Python Enum derived classes.
-Meanwhile all enums and flags have been converted to Python Enums
-(default since ``PySide 6.4``), see the :ref:`NewEnumSystem` section.
+ Meanwhile all enums and flags have been converted to Python Enums
+ (default since ``PySide 6.4``), see the :ref:`NewEnumSystem` section.
+PySide6.QtCore.Signal
+=====================
+
.. currentmodule:: PySide6.QtCore
+
.. py:class:: Signal
-Synopsis
---------
+ Synopsis
+ --------
+
+ Methods
+ ^^^^^^^
-Functions
-^^^^^^^^^
+ .. container:: function_list
-+-----------------------------------------------------+
-|def :meth:`connect<Signal.connect>` (receiver) |
-+-----------------------------------------------------+
-|def :meth:`disconnect<Signal.disconnect>` (receiver) |
-+-----------------------------------------------------+
-|def :meth:`emit<Signal.disconnect>` (\*args) |
-+-----------------------------------------------------+
+ * def :meth:`connect`
+ * def :meth:`disconnect`
+ * def :meth:`emit`
-Detailed Description
---------------------
+ Detailed Description
+ --------------------
-The :class:`~.Signal` class provides a way to declare and connect Qt
-signals in a pythonic way.
+ The :class:`~.Signal` class provides a way to declare and connect Qt
+ signals in a pythonic way.
-.. class:: PySide6.QtCore.Signal([type1 [, type2...]] [, name="" [, arguments=[]]])
+ .. seealso:: :ref:`tutorial_signals_and_slots`
+
+ .. py:method:: __init__([type1 [, type2...]] [, name="" [, arguments=[]]])
:param name: str
:param arguments: list
-``Signal`` takes a list of Python types of the arguments.
+ ``Signal`` takes a list of Python types of the arguments.
+
+ It is possible to use the same signal name with different types by
+ passing a list of tuples representing the signatures, but this is a legacy
+ technique recommended against in new code (see
+ :ref:`overloading-signals-and-slots`).
- It is possible to use the same signal name with different types by
- passing a list of tuples representing the signatures, but this is a legacy
- technique recommended against in new code (see
- :ref:`overloading-signals-and-slots`).
+ The optional named argument ``name`` defines the signal name. If nothing is
+ passed, the new signal will have the same name as the variable that it is
+ being assigned to.
-The optional named argument ``name`` defines the signal name. If nothing is
-passed, the new signal will have the same name as the variable that it is
-being assigned to.
+ The optional named argument ``arguments`` receives a list of strings
+ denoting the argument names. This is useful for QML applications which
+ may refer to the emitted values by name.
-The optional named argument ``arguments`` receives a list of strings
-denoting the argument names. This is useful for QML applications which
-may refer to the emitted values by name.
+ .. py:method:: connect(receiver[, type=Qt.AutoConnection])
-.. method:: Signal.connect(receiver[, type=Qt.AutoConnection])
+ :param receiver: Python callable, :deco:`Slot` or :class:`Signal`
+ :param type: :class:`~PySide6.QtCore.Qt.ConnectionType`
- Create a connection between this signal and a `receiver`, the `receiver`
- can be a Python callable, a :class:`Slot` or a :class:`Signal`.
+ Create a connection between this signal and a `receiver`.
-.. method:: Signal.disconnect(receiver)
+ .. py:method:: disconnect(receiver)
- Disconnect this signal from a `receiver`, the `receiver` can be a Python
- callable, a :class:`Slot` or a :class:`Signal`.
+ :param receiver: Python callable, :deco:`Slot` or :class:`Signal`
-.. method:: Signal.emit(*args)
+ Disconnect this signal from the `receiver`.
- `args` is the arguments to pass to any connected slots, if any.
+ .. py:method:: emit(*args)
-.. seealso:: :ref:`signals-and-slots`
+ Emits the signal. `args` is the arguments to pass to any connected slots,
+ if any.
+PySide6.QtCore.Slot
+===================
+
.. currentmodule:: PySide6.QtCore
.. py:decorator:: Slot([type1 [, type2...]] [, name="" [, result=None, [tag=""]]])
- :param name: str
- :param result: type
- :param tag: str
+ :param name: str
+ :param result: type
+ :param tag: str
-``Slot`` takes a list of Python types of the arguments.
+ ``Slot`` takes a list of Python types of the arguments.
-The optional named argument ``name`` defines the slot name. If nothing is
-passed, the slot name will be the decorated function name.
+ The optional named argument ``name`` defines the slot name. If nothing is
+ passed, the slot name will be the decorated function name.
-The optional named argument ``result`` specifies the return type.
+ The optional named argument ``result`` specifies the return type.
-The optional named argument ``tag`` specifies a value to be returned
-by ``QMetaMethod.tag()``.
+ The optional named argument ``tag`` specifies a value to be returned
+ by :meth:`QMetaMethod.tag`.
-This implementation is functionally compatible with the PyQt one.
+ This implementation is functionally compatible with the PyQt one.
-======= =========== ======
-Module PyQt PySide
-======= =========== ======
-QtCore pyqtSignal Signal
-QtCore pyqtSlot Slot
-======= =========== ======
+ ======= =========== ======
+ Module PyQt PySide
+ ======= =========== ======
+ QtCore pyqtSignal Signal
+ QtCore pyqtSlot Slot
+ ======= =========== ======
-.. seealso:: :ref:`signals-and-slots`
+ .. seealso:: :ref:`tutorial_signals_and_slots`
-Q_INVOKABLE
------------
+ Q_INVOKABLE
+ -----------
-There is no equivalent of the Q_INVOKABLE macro of Qt
-since PySide6 slots can actually have return values.
-If you need to create a invokable method that returns some value,
-declare it as a slot, e.g.:
+ There is no equivalent of the Q_INVOKABLE macro of Qt
+ since PySide6 slots can actually have return values.
+ If you need to create a invokable method that returns some value,
+ declare it as a slot, e.g.:
-::
+ ::
- class Foo(QObject):
- @Slot(float, result=int)
- def getFloatReturnInt(self, f):
- return int(f)
+ class Foo(QObject):
+ @Slot(float, result=int)
+ def getFloatReturnInt(self, f):
+ return int(f)
In addition, Qt Core provides a platform-independent mechanism for
storing binary files in the application's executable.
- * :ref:`The Qt Resource System<using_qrc_files>`
+ * :ref:`The Qt Resource System<tutorial_qrcfiles>`
Additional Frameworks
^^^^^^^^^^^^^^^^^^^^^
Qt Core also provides some of Qt's key frameworks.
* :ref:`The Animation Framework<The-Animation-Framework>`
- * `JSON Support in Qt <https://doc.qt.io/qt-6/json.html>`_
+ * :ref:`CBOR-Support-in-Qt`
+ * :ref:`JSON-Support-in-Qt`
+ * :ref:`Inter-Process-Communication`
* :ref:`The Event System<The-Event-System>`
+ * :ref:`Application-Permissions`
+
+List of Classes by Function
+---------------------------
+
+ * :ref:`Animation-Framework`
+ * :ref:`Event-Classes`
+ * :ref:`Input-Output-and-Networking`
import PySide6.QtDataVisualization
-The module also provides `QML types <https://doc.qt.io/qt-6/qtdatavisualization-qmlmodule.html>`_ .
-
Articles and Guides
^^^^^^^^^^^^^^^^^^^
+PySide6.QtDesigner.QPyDesignerContainerExtension
+================================================
+
.. currentmodule:: PySide6.QtDesigner
.. py:class:: QPyDesignerContainerExtension
-QPyDesignerContainerExtension is the base class for implementing
-`QDesignerContainerExtension class`_
-for a *Qt Widgets Designer* custom widget plugin in Python.
-It provides the required inheritance from **QObject**.
+ QPyDesignerContainerExtension is the base class for implementing
+ `QDesignerContainerExtension class`_
+ for a *Qt Widgets Designer* custom widget plugin in Python.
+ It provides the required inheritance from :class:`~PySide6.QtCore.QObject`.
.. _QDesignerContainerExtension class: https://doc.qt.io/qt-6/qdesignercontainerextension.html
+PySide6.QtDesigner.QPyDesignerCustomWidgetCollection
+====================================================
+
.. currentmodule:: PySide6.QtDesigner
.. py:class:: QPyDesignerCustomWidgetCollection
-Synopsis
---------
+ Synopsis
+ --------
+
+ Methods
+ ^^^^^^^
-Functions
-^^^^^^^^^
+ .. container:: function_list
-+------------------------------------------------------------------------------------------------+
-|def :meth:`registerCustomWidget<QPyDesignerCustomWidgetCollection.registerCustomWidget>` (type) |
-+------------------------------------------------------------------------------------------------+
-|def :meth:`addCustomWidget<QPyDesignerCustomWidgetCollection.addCustomWidget>` (custom_widget) |
-+------------------------------------------------------------------------------------------------+
+ def :meth:`registerCustomWidget`
+ def :meth:`addCustomWidget`
-Detailed Description
---------------------
+ Detailed Description
+ --------------------
-The :class:`~.QPyDesignerCustomWidgetCollection` implements
-`QDesignerCustomWidgetCollectionInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetcollectioninterface.html>`_
-and provides static helper functions for registering custom widgets by
-type or by implementing
-`QDesignerCustomWidgetInterface`_ .
+ The :class:`~.QPyDesignerCustomWidgetCollection` implements
+ `QDesignerCustomWidgetCollectionInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetcollectioninterface.html>`_
+ and provides static helper functions for registering custom widgets by
+ type or by implementing `QDesignerCustomWidgetInterface`_ .
-The usage is explained in :ref:`designer_custom_widgets`.
+ The usage is explained in :ref:`designer_custom_widgets`.
-.. py:staticmethod:: QPyDesignerCustomWidgetCollection.registerCustomWidget(type[, xml=""[, tool_tip=""[, icon=""[, group=""[container=False]]]]])
+ .. py:staticmethod:: QPyDesignerCustomWidgetCollection.registerCustomWidget(type[, xml=""[, tool_tip=""[, icon=""[, group=""[container=False]]]]])
- Registers an instance of a Python-implemented QWidget by type with
- *Qt Widgets Designer*.
+ :param str xml: A snippet of XML code in ``.ui`` format that specifies how the widget is created and sets initial property values.
+ :param str tool_tip: Tool tip to be shown in the widget box.
+ :param str icon: Path to an icon file be shown in the widget box.
+ :param str group: Category for grouping widgets in the widget box.
+ :param str module: Module name for generating the import code by `uic <https://doc.qt.io/qt-6/uic.html>`_ .
+ :param bool container: Indicates whether the widget is a container like `QGroupBox`, that is, child widgets can be placed on it.
- The optional keyword arguments correspond to the getters of
- `QDesignerCustomWidgetInterface`_ :
+ Registers an instance of a Python-implemented QWidget by type with
+ *Qt Widgets Designer*.
- :param str xml: A snippet of XML code in ``.ui`` format that specifies how the widget is created and sets initial property values.
- :param str tool_tip: Tool tip to be shown in the widget box.
- :param str icon: Path to an icon file be shown in the widget box.
- :param str group: Category for grouping widgets in the widget box.
- :param str module: Module name for generating the import code by `uic <https://doc.qt.io/qt-6/uic.html>`_ .
- :param bool container: Indicates whether the widget is a container like `QGroupBox`, that is, child widgets can be placed on it.
+ The optional keyword arguments correspond to the getters of
+ `QDesignerCustomWidgetInterface`_ :
- .. seealso:: :meth:`registerCustomWidget()<PySide6.QtUiTools.QUiLoader.registerCustomWidget>`
+ .. seealso:: :meth:`registerCustomWidget()<PySide6.QtUiTools.QUiLoader.registerCustomWidget>`
-.. py:staticmethod:: QPyDesignerCustomWidgetCollection.addCustomWidget(custom_widget)
+ .. py:staticmethod:: QPyDesignerCustomWidgetCollection.addCustomWidget(custom_widget)
- Adds a custom widget (implementation of
- `QDesignerCustomWidgetInterface`_)
- with *Qt Widgets Designer*.
+ :param QDesignerCustomWidgetInterface custom_widget: Custom widget instance
- :param QDesignerCustomWidgetInterface custom_widget: Custom widget instance
+ Adds a custom widget (implementation of
+ `QDesignerCustomWidgetInterface`_)
+ with *Qt Widgets Designer*.
.. _QDesignerCustomWidgetInterface: https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html
+PySide6.QtDesigner.QPyDesignerMemberSheetExtension
+==================================================
+
.. currentmodule:: PySide6.QtDesigner
.. py:class:: QPyDesignerMemberSheetExtension
-QPyDesignerMemberSheetExtension is the base class for implementing
-`QDesignerMemberSheetExtension class`_
-for a *Qt Widgets Designer* custom widget plugin in Python.
-It provides the required inheritance from **QObject**.
+ QPyDesignerMemberSheetExtension is the base class for implementing
+ `QDesignerMemberSheetExtension class`_
+ for a *Qt Widgets Designer* custom widget plugin in Python.
+ It provides the required inheritance from :class:`~PySide6.QtCore.QObject`.
.. _QDesignerMemberSheetExtension class: https://doc.qt.io/qt-6/qdesignermembersheetextension.html
+PySide6.QtDesigner.QPyDesignerTaskMenuExtension
+===============================================
+
.. currentmodule:: PySide6.QtDesigner
.. py:class:: QPyDesignerTaskMenuExtension
-QPyDesignerTaskMenuExtension is the base class for implementing
-`QDesignerTaskMenuExtension class`_
-for a *Qt Widgets Designer* custom widget plugin in Python.
-It provides the required inheritance from **QObject**.
+ QPyDesignerTaskMenuExtension is the base class for implementing
+ `QDesignerTaskMenuExtension class`_
+ for a *Qt Widgets Designer* custom widget plugin in Python.
+ It provides the required inheritance from :class:`~PySide6.QtCore.QObject`.
.. _QDesignerTaskMenuExtension class: https://doc.qt.io/qt-6/qdesignertaskmenuextension.html
^^^^^^^^^^^^^^^^^^^
The most important classes in the Qt GUI module are
-:class:`QGuiApplication<PySide6.QtGui.QGuiApplication>` and
-:class:`QWindow<PySide6.QtGui.QWindow>` . A Qt application that wants
-to show content on screen will need to make use of these.
+:class:`QGuiApplication<PySide6.QtGui.QGuiApplication>`,
+:class:`~PySide6.QtGui.QWindow` and :class:`~PySide6.QtGui.QScreen`.
+A Qt application that wants to show content on screen will need to make
+use of these.
:class:`QGuiApplication<PySide6.QtGui.QGuiApplication>` contains the
main event loop, where all events from the window system and other
sources are processed and dispatched. It also handles the
have OpenGL hardware-accelerated 2D graphics by sacrificing some of
the visual quality.
-Vulkan Integration
-^^^^^^^^^^^^^^^^^^
-
-Qt GUI has support for the `Vulkan <https://www.khronos.org/vulkan/>`_
-API. Qt applications require the presence of the `LunarG Vulkan SDK
-<https://www.lunarg.com/vulkan-sdk/>`_ .
-
-On Windows, the SDK sets the environment variable ``VULKAN_SDK``\,
-which will be detected by the ``configure`` script.
-
-On Android, Vulkan headers were added in API level 24 of the NDK.
-
-Relevant classes:
-
-* QVulkanDeviceFunctions
- * :class:`QVulkanExtension<~.QVulkanExtension>`
- * QVulkanFunctions
- * :class:`QVulkanInfoVector<~.QVulkanInfoVector>`
- * :class:`QVulkanInstance<~.QVulkanInstance>`
- * :class:`QVulkanWindow<~.QVulkanWindow>`
- * :class:`QVulkanWindowRenderer<~.QVulkanWindowRenderer>`
-
-For more information, see the
-:ref:`Hello Vulkan Widget Example<Hello-Vulkan-Widget-Example>` and the
-:ref:`Hello Vulkan Window Example<Hello-Vulkan-Window-Example>` .
-
Drag and Drop
^^^^^^^^^^^^^
::
import PySide6.QtGui
+
+List of Classes by Function
+---------------------------
+
+ * :ref:`Painting-Classes`
+ * :ref:`Rendering-in-3D`
+ * :ref:`Rich-Text-Processing-APIs`
import PySide6.QtMultimedia
-The module also provides QML `types <https://doc.qt.io/qt-6/qtmultimedia-qmlmodule.html>`_ .
-
Overviews and Important Topics
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Also note shipping OpenSSL might cause
`Import and Export Restrictions <https://doc.qt.io/qt-6/ssl.html#import-and-export-restrictions>`_
to apply.
+
+List of Classes by Function
+---------------------------
+
+ * :ref:`Network-Programming-API`
The QtOpenGL module is available on Windows, X11 and Mac OS X. Qt for
Embedded Linux and OpenGL supports OpenGL ES (OpenGL for Embedded
Systems).
-
import PySide6.QtPositioning
-The module also provides `QML types <https://doc.qt.io/qt-6/qtpositioning-qmlmodule.html>`_ .
-
To load the Qt Positioning module, add the following statement to your .qml files
::
::
import PySide6.QtPrintSupport
+
+List of Classes by Function
+---------------------------
+
+ * :ref:`Printer-and-Printing-APIs`
+PySide6.QtQml.ListProperty
+==========================
+
.. currentmodule:: PySide6.QtQml
.. py:class:: ListProperty
The ``ListProperty`` class allows applications to expose list-like properties of
:class:`~PySide6.QtCore.QObject`-derived classes to QML.
- The usage is shown in the :ref:`qml-object-and-list-property-types-example`
- and the :ref:`qml-chapter5-listproperties` example.
+ The usage is shown in the :ref:`example_qml_tutorials_extending-qml-advanced_properties`
+ and the :ref:`example_qml_tutorials_extending-qml_chapter5-listproperties` example.
.. py:method:: __init__(type, append, count=None, at=None, clear=None, removeLast=None, doc="", notify=None, designable=True, scriptable=True, stored=True, user=False, constant=False, final=False)
.. currentmodule:: PySide6.QtQml
.. py:class:: QPyQmlParserStatus
-QPyQmlParserStatus is the base class for implementing
-`QQmlParserStatus class`_ .
+ QPyQmlParserStatus is the base class for implementing
+ `QQmlParserStatus class`_ .
-It provides the required inheritance from **QObject**.
+ It provides the required inheritance from :class:`~PySide6.QtCore.QObject`.
.. _QQmlParserStatus class: https://doc.qt.io/qt-6/qqmlparserstatus.html
.. currentmodule:: PySide6.QtQml
.. py:class:: QPyQmlPropertyValueSource
-QPyQmlPropertyValueSource is the base class for implementing
-`QQmlPropertyValueSource class`_ .
+ QPyQmlPropertyValueSource is the base class for implementing
+ `QQmlPropertyValueSource class`_ .
-It provides the required inheritance from **QObject**.
+ It provides the required inheritance from :class:`~PySide6.QtCore.QObject`.
.. _QQmlPropertyValueSource class: https://doc.qt.io/qt-6/qqmlpropertyvaluesource.html
+PySide6.QtQml.QmlAnonymous
+==========================
+
.. currentmodule:: PySide6.QtQml
.. py:decorator:: QmlAnonymous
-Declares the enclosing type to be available, but anonymous in QML. The type
-cannot be created or used to declare properties in QML, but when passed from
-C++, it is recognized. In QML, you can use properties of this type if they
-are declared in C++.
+ Declares the enclosing type to be available, but anonymous in QML. The type
+ cannot be created or used to declare properties in QML, but when passed from
+ C++, it is recognized. In QML, you can use properties of this type if they
+ are declared in C++.
-.. code-block:: python
+ .. code-block:: python
- QML_IMPORT_NAME = "com.library.name"
- QML_IMPORT_MAJOR_VERSION = 1
- QML_IMPORT_MINOR_VERSION = 0 # Optional
+ QML_IMPORT_NAME = "com.library.name"
+ QML_IMPORT_MAJOR_VERSION = 1
+ QML_IMPORT_MINOR_VERSION = 0 # Optional
- @QmlAnonymous
- class ClassForQml(QObject):
- # ...
+ @QmlAnonymous
+ class ClassForQml(QObject):
+ # ...
+PySide6.QtQml.QmlAttached
+=========================
+
.. currentmodule:: PySide6.QtQml
.. py:decorator:: QmlAttached
-This decorator declares that the enclosing type attaches the type passed as
-an attached property to other types. This takes effect if the type is exposed
-to QML using a ``QmlElement()`` or ``@QmlNamedElement()`` decorator.
+ This decorator declares that the enclosing type attaches the type passed as
+ an attached property to other types. This takes effect if the type is exposed
+ to QML using a :deco:`QmlElement` or :deco:`QmlNamedElement` decorator.
-.. code-block:: python
+ .. code-block:: python
- QML_IMPORT_NAME = "com.library.name"
- QML_IMPORT_MAJOR_VERSION = 1
- QML_IMPORT_MINOR_VERSION = 0 # Optional
+ QML_IMPORT_NAME = "com.library.name"
+ QML_IMPORT_MAJOR_VERSION = 1
+ QML_IMPORT_MINOR_VERSION = 0 # Optional
- @QmlAnonymous
- class LayoutAttached(QObject):
- @Property(QMargins)
- def margins(self):
- ...
+ @QmlAnonymous
+ class LayoutAttached(QObject):
+ @Property(QMargins)
+ def margins(self):
+ ...
- @QmlElement()
- @QmlAttached(LayoutAttached)
- class Layout(QObject):
- ...
+ @QmlElement()
+ @QmlAttached(LayoutAttached)
+ class Layout(QObject):
+ ...
-Afterwards the class may be used in QML:
+ Afterwards the class may be used in QML:
-.. code-block:: javascript
+ .. code-block:: javascript
- import com.library.name 1.0
+ import com.library.name 1.0
- Layout {
- Widget {
- Layout.margins: [2, 2, 2, 2]
- }
- }
+ Layout {
+ Widget {
+ Layout.margins: [2, 2, 2, 2]
+ }
+ }
+PySide6.QtQml.QmlElement
+========================
+
.. currentmodule:: PySide6.QtQml
.. py:decorator:: QmlElement
-This decorator registers a class it is attached to for use in QML, using
-global variables to specify the import name and version.
+ This decorator registers a class it is attached to for use in QML, using
+ global variables to specify the import name and version.
-.. code-block:: python
+ .. code-block:: python
- QML_IMPORT_NAME = "com.library.name"
- QML_IMPORT_MAJOR_VERSION = 1
- QML_IMPORT_MINOR_VERSION = 0 # Optional
+ QML_IMPORT_NAME = "com.library.name"
+ QML_IMPORT_MAJOR_VERSION = 1
+ QML_IMPORT_MINOR_VERSION = 0 # Optional
- @QmlElement
- class ClassForQml(QObject):
- # ...
+ @QmlElement
+ class ClassForQml(QObject):
+ # ...
-Afterwards the class may be used in QML:
+ Afterwards the class may be used in QML:
-.. code-block:: python
+ .. code-block:: python
- import com.library.name 1.0
+ import com.library.name 1.0
- ClassForQml {
- // ...
- }
+ ClassForQml {
+ // ...
+ }
+PySide6.QtQml.QmlExtended
+=========================
+
.. currentmodule:: PySide6.QtQml
.. py:decorator:: QmlExtended
-Declares that the enclosing type uses the type passed as an extension to
-provide further properties, methods, and enumerations in QML. This takes effect
-if the type is exposed to QML using a ``QmlElement()`` or ``QmlNamedElement()``
-decorator.
+ Declares that the enclosing type uses the type passed as an extension to
+ provide further properties, methods, and enumerations in QML. This takes effect
+ if the type is exposed to QML using a :deco:`QmlElement` or :deco:`QmlNamedElement`
+ decorator.
-.. code-block:: python
+ .. code-block:: python
- QML_IMPORT_NAME = "com.library.name"
- QML_IMPORT_MAJOR_VERSION = 1
- QML_IMPORT_MINOR_VERSION = 0 # Optional
+ QML_IMPORT_NAME = "com.library.name"
+ QML_IMPORT_MAJOR_VERSION = 1
+ QML_IMPORT_MINOR_VERSION = 0 # Optional
- class LineEditExtension(QObject):
- pass
+ class LineEditExtension(QObject):
+ pass
- @QmlNamedElement("QLineEdit")
- @QmlExtended(LineEditExtension)
- @QmlForeign(QLineEdit)
- class LineEditForeign(QObject):
- ...
+ @QmlNamedElement("QLineEdit")
+ @QmlExtended(LineEditExtension)
+ @QmlForeign(QLineEdit)
+ class LineEditForeign(QObject):
+ ...
-Afterwards the class may be used in QML:
+ Afterwards the class may be used in QML:
-.. code-block:: javascript
+ .. code-block:: javascript
- import com.library.name 1.0
+ import com.library.name 1.0
- QLineEdit {
- left_margin: 10
- }
+ QLineEdit {
+ left_margin: 10
+ }
+PySide6.QtQml.QmlForeign
+========================
+
.. currentmodule:: PySide6.QtQml
.. py:decorator:: QmlForeign
-This decorator can be used to change the type that is created by QML.
+ This decorator can be used to change the type that is created by QML.
-This is useful for registering types that cannot be amended by adding the
-QmlElement decorator, for example because they belong to 3rdparty libraries.
+ This is useful for registering types that cannot be amended by adding the
+ :deco:`QmlElement` decorator, for example because they belong to 3rdparty libraries.
-.. code-block:: python
+ .. code-block:: python
- QML_IMPORT_NAME = "com.library.name"
- QML_IMPORT_MAJOR_VERSION = 1
- QML_IMPORT_MINOR_VERSION = 0 # Optional
+ QML_IMPORT_NAME = "com.library.name"
+ QML_IMPORT_MAJOR_VERSION = 1
+ QML_IMPORT_MINOR_VERSION = 0 # Optional
- @QmlNamedElement("QWidget")
- @QmlForeign(QWidget)
- class ForeignWidgetHelperClass(QObject):
- ...
+ @QmlNamedElement("QWidget")
+ @QmlForeign(QWidget)
+ class ForeignWidgetHelperClass(QObject):
+ ...
-Afterwards the class may be used in QML:
+ Afterwards the class may be used in QML:
-.. code-block:: javascript
+ .. code-block:: javascript
- import com.library.name 1.0
+ import com.library.name 1.0
- QWidget {
- // ...
- }
+ QWidget {
+ // ...
+ }
.. currentmodule:: PySide6.QtQml
.. py:decorator:: QmlNamedElement
-This decorator registers a class it is attached to for use in QML under
-a name different from the class name, using global variables to specify
-the import name and version.
+ This decorator registers a class it is attached to for use in QML under
+ a name different from the class name, using global variables to specify
+ the import name and version.
-.. code-block:: python
+ .. code-block:: python
- QML_IMPORT_NAME = "com.library.name"
- QML_IMPORT_MAJOR_VERSION = 1
- QML_IMPORT_MINOR_VERSION = 0 # Optional
+ QML_IMPORT_NAME = "com.library.name"
+ QML_IMPORT_MAJOR_VERSION = 1
+ QML_IMPORT_MINOR_VERSION = 0 # Optional
- @QmlNamedElement("ClassForQml")
- class ClassWithSomeName(QObject):
- ...
+ @QmlNamedElement("ClassForQml")
+ class ClassWithSomeName(QObject):
+ ...
-Afterwards the class may be used in QML:
+ Afterwards the class may be used in QML:
-.. code-block:: javascript
+ .. code-block:: javascript
- import com.library.name 1.0
+ import com.library.name 1.0
- ClassForQml {
- // ...
- }
+ ClassForQml {
+ // ...
+ }
.. currentmodule:: PySide6.QtQml
.. py:decorator:: QmlSingleton
-Declares the decorated type to be a singleton in QML. This only takes effect if
-the type is a QObject and is available in QML (by having a QmlElement decorator).
-The QQmlEngine will try to create a singleton instance using the type's default
-constructor.
+ Declares the decorated type to be a singleton in QML. This only takes effect if
+ the type is a :class:`~PySide6.QtCore.QObject` and is available in QML
+ (by having a :deco:`QmlElement` decorator).
+ The :class:`QQmlEngine` will try to create a singleton instance using the type's default
+ constructor.
-.. code-block:: python
+ .. code-block:: python
- QML_IMPORT_NAME = "com.library.name"
- QML_IMPORT_MAJOR_VERSION = 1
- QML_IMPORT_MINOR_VERSION = 0 # Optional
+ QML_IMPORT_NAME = "com.library.name"
+ QML_IMPORT_MAJOR_VERSION = 1
+ QML_IMPORT_MINOR_VERSION = 0 # Optional
- @QmlElement
- @QmlSingleton
- class ClassForQml(QObject):
- ...
+ @QmlElement
+ @QmlSingleton
+ class ClassForQml(QObject):
+ ...
-It is also possible to use a static ``create()`` method which receives
-the engine as a parameter:
+ It is also possible to use a static ``create()`` method which receives
+ the engine as a parameter:
-.. code-block:: python
+ .. code-block:: python
- @QmlElement
- @QmlSingleton
- class ClassForQml(QObject):
+ @QmlElement
+ @QmlSingleton
+ class ClassForQml(QObject):
- @staticmethod
- def create(engine):
- ...
+ @staticmethod
+ def create(engine):
+ ...
-.. note:: The order of the decorators matters; ``QmlSingleton`` needs to be preceded by ``QmlElement``.
+ .. note:: The order of the decorators matters; ``QmlSingleton`` needs to be preceded by ``QmlElement``.
.. currentmodule:: PySide6.QtQml
.. py:decorator:: QmlUncreatable
-Declares that the decorated type shall not be creatable from QML. This takes
-effect if the type is available in QML, by a preceding ``QmlElement``
-decorator. The reason will be emitted as error message if an attempt to create
-the type from QML is detected.
+ Declares that the decorated type shall not be creatable from QML. This takes
+ effect if the type is available in QML, by a preceding :deco:`QmlElement`
+ decorator. The reason will be emitted as error message if an attempt to create
+ the type from QML is detected.
-Some QML types are implicitly uncreatable, in particular types exposed with
-``QmlAnonymous``.
+ Some QML types are implicitly uncreatable, in particular types exposed with
+ :deco:`QmlAnonymous`.
-Passing None or no argument will cause a standard message to be used instead.
+ Passing None or no argument will cause a standard message to be used instead.
-.. code-block:: python
+ .. code-block:: python
- QML_IMPORT_NAME = "com.library.name"
- QML_IMPORT_MAJOR_VERSION = 1
- QML_IMPORT_MINOR_VERSION = 0 # Optional
+ QML_IMPORT_NAME = "com.library.name"
+ QML_IMPORT_MAJOR_VERSION = 1
+ QML_IMPORT_MINOR_VERSION = 0 # Optional
- @QmlElement
- @QmlUncreatable("BaseClassForQml is an abstract base class")
- class BaseClassForQml(QObject):
- ...
+ @QmlElement
+ @QmlUncreatable("BaseClassForQml is an abstract base class")
+ class BaseClassForQml(QObject):
+ ...
-.. note:: The order of the decorators matters; ``QmlUncreatable`` needs to be preceded by ``QmlElement``.
+ .. note:: The order of the decorators matters; ``QmlUncreatable`` needs to be preceded by ``QmlElement``.
-The Qt QML module defines and implements the QML language
+The Qt Qml module implements the QML language and offers APIs to register types
+for it.
-The Qt QML module provides a framework for developing applications and
-libraries with the QML language. It defines and implements the language and
-engine infrastructure, and provides an API to enable application developers to
-extend the QML language with custom types and integrate QML code with
-JavaScript and C++. The Qt QML module provides both a `QML API
-<https://doc.qt.io/qt-6/qtqml-qmlmodule.html>`_ and a `C++ API
-<https://doc.qt.io/qt-6/qtqml-module.html>`_ .
-
-Note that while the Qt QML module provides the language and infrastructure for
-QML applications, the :ref:`Qt Quick<Qt-Quick>` module provides many visual
-components, model-view support, an animation framework, and much more for
-building user interfaces.
-
-For those new to QML and Qt Quick, please see QML Applications for an
-introduction to writing QML applications.
+The Qt Qml module provides a framework for developing applications and
+libraries with the :ref:`The-QML-Reference` . It defines and implements the
+language and engine infrastructure, and provides an API to enable application
+developers to register custom QML types and modules and integrate QML code with
+JavaScript and Python. The Qt Qml module provides both a QML API a Python API.
Using the Module
^^^^^^^^^^^^^^^^
import PySide6.QtQml
-The module also provides `QML types <https://doc.qt.io/qt-6/qtqml-qmlmodule.html>`_ .
-
-QML and QML Types
-^^^^^^^^^^^^^^^^^
-
-The Qt QML module contains the QML framework and important QML types used in
-applications. The constructs of QML are described in the
-:ref:`The QML Reference<The-QML-Reference>` .
-
-In addition to the :ref:`QML Basic Types<QML-Basic-Types>` , the module comes
-with the following QML object types:
-
- * `Component <https://doc.qt.io/qt-6/qml-qtqml-component.html>`_
- * `QtObject <https://doc.qt.io/qt-6/qml-qtqml-qtobject.html>`_
- * `Binding <https://doc.qt.io/qt-6/qml-qtqml-binding.html>`_
- * `Connections <https://doc.qt.io/qt-6/qml-qtqml-connections.html>`_
- * `Timer <https://doc.qt.io/qt-6/qml-qtqml-timer.html>`_
-
-The `Qt <https://doc.qt.io/qt-6/qml-qtqml-qt.html>`_ global object provides
-useful enums and functions for various QML types.
-
-Lists and Models
-^^^^^^^^^^^^^^^^
-
-New in Qt 5.1, the model types are moved to a submodule, ``QtQml.Models``\. The
-Qt QML Models page has more information.
-
- * DelegateModel
- * DelegateModelGroup
- * ListElement
- * ListModel
- * ObjectModel
-
-JavaScript Environment for QML Applications
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Registering QML Types and QML Modules
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-JavaScript expressions allow QML code to contain application logic. Qt QML
-provides the framework for running JavaScript expressions in QML and from C++.
+See :ref:`tutorial_qmlintegration`.
-These sections are from :ref:`The QML Reference<The-QML-Reference>` .
+Tweaking the engine
+^^^^^^^^^^^^^^^^^^^
- * `Integrating QML and JavaScript <https://doc.qt.io/qt-6/qtqml-javascript-topic.html>`_
- * `Using JavaScript Expressions with QML <https://doc.qt.io/qt-6/qtqml-javascript-expressions.html>`_
- * `Dynamic QML Object Creation from JavaScript <https://doc.qt.io/qt-6/qtqml-javascript-dynamicobjectcreation.html>`_
- * `Defining JavaScript Resources In QML <https://doc.qt.io/qt-6/qtqml-javascript-resources.html>`_
- * `Importing JavaScript Resources In QML <https://doc.qt.io/qt-6/qtqml-javascript-imports.html>`_
- * `JavaScript Host Environment <https://doc.qt.io/qt-6/qtqml-javascript-hostenvironment.html>`_
+There are a number of knobs you can turn to customize the behavior of the QML
+engine. The page on :ref:`Configuring-the-JavaScript-Engine` lists the
+environment variables you may use to this effect. The description of
+:ref:`The-QML-Disk-Cache` describes the options related to how your QML
+components are compiled and loaded.
--- /dev/null
+The Qt Quick module implements the "standard library" for QML
+
+The Qt Quick module is the standard library for writing QML applications. While
+the Qt Qml module provides the QML engine and language infrastructure, the Qt
+Quick module provides all the basic types necessary for creating user
+interfaces with QML. It provides a visual canvas and includes types for
+creating and animating visual components, receiving user input, creating data
+models and views and delayed object instantiation.
+
+The Qt Quick module provides both a QML API, which supplies QML types for
+creating user interfaces with the QML language, and a Python API for extending
+QML applications with Python code.
+
+.. note:: A set of Qt Quick-based UI controls is also available to create user interfaces.
+ See :mod:`PySide6.QtQuickControls2` for more information.
+
+If you're new to QML and Qt Quick, please see :ref:`QML-Applications` for an
+introduction to writing QML applications.
+
+Using the Module
+^^^^^^^^^^^^^^^^
+
+To include the definitions of modules classes, use the following
+directive:
+
+::
+
+ import PySide6.QtQuick
+
+Important Concepts in Qt Quick
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Qt Quick provides everything you need to create a rich application with a fluid
+and dynamic user interface. It enables you to build user interfaces around the
+behavior of user interface components and how they connect with one another,
+and it provides a visual canvas with its own coordinate system and rendering
+engine. Animation and transition effects are first class concepts in Qt Quick,
+and you can add visual effects through specialized components for particle and
+shader effects.
+
+ * :ref:`Important-Concepts-In-Qt-Quick---The-Visual-Canvas`
+ * :ref:`Important-Concepts-In-Qt-Quick---User-Input`
+ * :ref:`Important-Concepts-In-Qt-Quick---Positioning`
+ * :ref:`Important-Concepts-in-Qt-Quick---States--Transitions-and-Animations`
+ * :ref:`Important-Concepts-In-Qt-Quick---Data---Models--Views-and-Data-Storage`
+ * :ref:`Important-Concepts-In-Qt-Quick---Graphical-Effects`
+ * `MultiEffect <https://doc.qt.io/qt-6/qml-qtquick-effects-multieffect.html>`_
+ * :ref:`Important-Concepts-In-Qt-Quick---Convenience-Types`
+
+When using the Qt Quick module, you will need to know how to write QML
+applications using the QML language. In particular, QML Basics and QML
+Essentials from the QML Applications page.
+
+To find out more about using the QML language, see :mod:`Qt Qml <PySide6.QtQml>`.
+
+C++ Extension Points
+^^^^^^^^^^^^^^^^^^^^
+
+ * :ref:`C++ Extension Points<C---Extension-Points-Provided-By-Qt-Quick>`
+
+Articles and Guides
+^^^^^^^^^^^^^^^^^^^
+
+ * :ref:`Qt Quick Guidelines<Best-Practices-for-QML-and-Qt-Quick>`
+ * :ref:`Qt Quick Tools and Utilities<Qt-Quick-Tools-and-Utilities>`
+
+Further information for writing QML applications:
+
+ * :ref:`QML-Applications` - Essential information for application development with QML and Qt Quick
+ * :mod:`Qt Qml <PySide6.QtQml>` - Documentation for the Qt QML module, which provides the QML engine and language infrastructure
+ * :ref:`Qt Quick How-tos<Qt-Quick-How-tos>` - shows how to achieve specific tasks in Qt Quick
+
+Qt Academy Courses
+^^^^^^^^^^^^^^^^^^
+
+ * `Introduction to Qt Quick <https://www.qt.io/academy/course-catalog#introduction-to-qt-quick>`_
+
+List of Classes by Function
+---------------------------
+
+ * :ref:`Qt-Quick-Scene-Graph-Material-Classes`
+ * :ref:`Qt-Quick-Scene-Graph-Node-classes`
Qt Quick Controls provides a set of controls that can be used to build complete
interfaces in Qt Quick. The module was introduced in Qt 5.7.
- .. image:: images/qtquickcontrols2-styles.png
+ .. image:: images/qtquickcontrols-styles.png
Qt Quick Controls comes with a selection customizable styles. See
:ref:`Styling Qt Quick Controls<Styling-Qt-Quick-Controls>` for more details.
QML API
^^^^^^^
-The `QML types <https://doc.qt.io/qt-6/qttest-qmlmodule.html>`_
-in Qt Quick Test are available through the ``QtTest`` import.
+The QML types in Qt Quick Test are available through the ``QtTest`` import.
To use the types, add the following import statement to your ``.qml`` file:
.. code-block:: JavaScript
::
import PySide6.QtSensors
-
-The module also provides `QML types <https://doc.qt.io/qt-6/qtsensors-qmlmodule.html>`_ .
::
import PySide6.QtSql
+
+List of Classes by Function
+---------------------------
+
+ * :ref:`Database-Classes`
^^^^^^^^^^^^^
* `JavaScript API <https://doc.qt.io/qt-6/qtwebchannel-javascript.html>`_
-
-The module also provides `QML types <https://doc.qt.io/qt-6/qtwebchannel-qmlmodule.html>`_ .
import PySide6.QtWebSockets
-The module also provides `QML types <https://doc.qt.io/qt-6/qtwebsockets-qmlmodule.html>`_ .
-
Articles and Guides
^^^^^^^^^^^^^^^^^^^
--- /dev/null
+
+Qt WebView lets you display web content inside a QML application. To avoid including a full web
+browser stack, Qt WebView uses native APIs where appropriate.
+
+Getting Started
+^^^^^^^^^^^^^^^
+
+To include the definitions of modules classes, use the following
+directive:
+
+ ::
+
+ from PySide6.QtWebView import QtWebView
+
+To make the Qt WebView module function correctly across all platforms, it's
+necessary to call ``QtWebView.initialize()`` before creating the QGuiApplication
+instance and before window's QPlatformOpenGLContext is created. For usage,
+see the ``minibrowser`` example in the PySide6 examples package.
+
+API Reference
+^^^^^^^^^^^^^
+
+ * `Qt API <https://doc.qt.io/qt-6/qtwebview-index.html>`_
+
+The module also provides `QML types <https://doc.qt.io/qt-6/qtwebview-index.html#qml-api>`_
:meth:`sizePolicy<PySide6.QtWidgets.QWidget.sizePolicy>` properties,
and the layout distributes the available space accordingly.
-:ref:`Qt Widgets Designer<using_ui_files>` is a powerful tool for interactively
+:ref:`Qt Widgets Designer<tutorial_uifiles>` is a powerful tool for interactively
creating and arranging widgets in layouts.
Model/View Classes
applications which use lists and tables are structured to separate the
data and view using models, views, and delegates.
- .. image:: images/windows-treeview.png
+ .. image:: images/fusion-treeview.png
Graphics View
^^^^^^^^^^^^^
::
import PySide6.QtWidgets
+
+List of Classes by Function
+---------------------------
+
+ * :ref:`Widgets-Classes`
--- /dev/null
+.. _faq-porting-from2:
+
+Porting Applications from PySide2 to PySide6
+============================================
+
+Module Availability
+-------------------
+
+Qt for Python 6.2.0 provides all modules planned for inclusion in Qt 6.
+
+Module-Level Changes
+--------------------
+
+* The modules *QtMacExtras*, *Qt Quick Controls 1*, *QtWinExtras*,
+ *QtXmlPatterns* and *QtX11Extras* have been removed.
+* ``QStateMachine`` and related classes have been extracted to a new
+ *QtStateMachine* module.
+* The modules *QtWebKit* and *QtWebKitWidgets* have been replaced by the new
+ *QtWebEngineCore*, *QtWebEngineQuick* and *QtWebEngineWidgets* modules.
+* ``QXmlReader`` and related classes (*SAX API*) have been removed.
+* The content of the *QtOpenGL* module has been replaced. The class
+ ``QGLWidget`` and related classes (``QGLContext``, ``QGLFunctions``,
+ ``QGLShaderProgram``) have been removed. Parts of the *Open GL*
+ functionality from *QtGui* have been extracted into this module, for example
+ ``QOpenGLBuffer`` and ``QOpenGLShaderProgram``.
+ There is a new module *QtOpenGLWidgets* which contains the class
+ ``QOpenGLWidget``, a replacement for ``QGLWidget``.
+
+As *Open GL* is phasing out,
+`QRhi <https://doc.qt.io/qt-6/topics-graphics.html>`_ should be considered
+for graphics applications.
+
+Imports
+-------
+
+The first thing to do when porting applications is to replace the
+import statements:
+
+.. code-block:: python
+
+ from PySide2.QtWidgets import QApplication
+ from PySide2 import QtCore
+
+needs to be changed to:
+
+.. code-block:: python
+
+ from PySide6.QtWidgets import QApplication
+ from PySide6 import QtCore
+
+
+Some classes are in a different module now, for example
+``QAction`` and ``QShortcut`` have been moved from ``QtWidgets`` to ``QtGui``.
+
+For *Qt Charts* and *Qt Data Visualization*, the additional namespaces have been
+removed. It is now possible to use:
+
+.. code-block:: python
+
+ from PySide6.QtCharts import QChartView
+
+directly.
+
+
+Class/Function Deprecations
+---------------------------
+
+Then, the code base needs to be checked for usage of deprecated API and adapted
+accordingly. For example:
+
+* The High DPI scaling attributes ``Qt.AA_EnableHighDpiScaling``,
+ ``Qt.AA_DisableHighDpiScaling`` and ``Qt.AA_UseHighDpiPixmaps`` are
+ deprecated. High DPI is by default enabled in Qt 6 and cannot be turned off.
+* ``QDesktopWidget`` has been removed. ``QScreen`` should be used instead,
+ which can be retrieved using ``QWidget.screen()``,
+ ``QGuiApplication.primaryScreen()`` or ``QGuiApplication.screens()``.
+* ``QFontMetrics.width()`` has been renamed to ``horizontalAdvance()``.
+* ``QMouseEvent.pos()`` and ``QMouseEvent.globalPos()`` returning a ``QPoint``
+ as well as ``QMouseEvent.x()`` and ``QMouseEvent.y()`` returning ``int``
+ are now deprecated. ``QMouseEvent.position()`` and
+ ``QMouseEvent.globalPosition()`` returning a ``QPointF`` should be used
+ instead.
+* ``Qt.MidButton`` has been renamed to ``Qt.MiddleButton``.
+* ``QOpenGLVersionFunctionsFactory.get()`` instead of
+ ``QOpenGLContext.versionFunctions()`` should be used to obtain
+ *Open GL* functions.
+* ``QRegExp`` has been replaced by ``QRegularExpression``.
+* ``QWidget.mapToGlobal()`` and ``QWidget.mapFromGlobal()`` now also accept
+ and return ``QPointF``.
+* Functions named ``exec_`` (classes ``QCoreApplication``, ``QDialog``,
+ ``QEventLoop``) have been renamed to ``exec`` which became possible
+ in Python 3.
+
+More information can be found in the
+`Porting to Qt 6 <https://doc.qt.io/qt-6/portingguide.html>`_ Guide
+and the `Qt 6.2 Documentation <https://doc.qt.io/qt-6/index.html>`_ .
which you can import from your main files, so it is not necessary
for you to include the `.ui` files in your deployed application.
-For more details, see :ref:`using_ui_files`.
+For more details, see :ref:`tutorial_uifiles`.
Resource Collection Files ``.qrc``
----------------------------------
so you are not required to include the listed files in your deployed
application.
-For more details, see :ref:`using_qrc_files`.
+For more details, see :ref:`tutorial_qrcfiles`.
Qt Modeling Language File ``.qml``
----------------------------------
.. note:: You can adapt these applications to use your self-made style, but
you need to be aware that the goal of Widgets is to respect the system
style, be careful when changing colors. Check this
- :ref:`simple tutorial <widgetstyling>` on how to do so.
+ :ref:`simple tutorial <tutorial_widgetstyling>` on how to do so.
QML
---
--- /dev/null
+.. _getting-started:
+
+Getting Started
+===============
+
+Here you can find the steps to install and create a simple application
+using the two technologies that Qt provides: Qt Widgets and Qt Quick.
+
+.. note:: If you are new to Qt, you can check the :ref:`faq-section` section at
+ the end of this page to understand concepts, file types, compatibles IDEs,
+ etc. In case you own a Qt License, please refer to :ref:`commercial-page`.
+
+Requirements
+------------
+
+Before you can install |project|, first you must install the following software:
+
+* `Official <https://www.python.org/downloads/>`_ Python 3.8+
+* We **highly** recommend using a virtual environment, such as
+ `venv <https://docs.python.org/3/library/venv.html>`_ or
+ `virtualenv <https://virtualenv.pypa.io/en/latest>`_
+ and avoid installing PySide6 via ``pip`` in your system.
+
+Installation
+------------
+
+* **Creating and activating an environment**
+ You can do this by running the following on a terminal:
+
+ * Create environment (Your Python executable might be called ``python3``)::
+
+ python -m venv env
+
+ * Activate the environment (Linux and macOS)::
+
+ source env/bin/activate
+
+ * Activate the environment (Windows)::
+
+ env\Scripts\activate.bat
+
+ Check this animation on how to do it:
+
+ .. image:: https://qt-wiki-uploads.s3.amazonaws.com/images/8/8a/Pyside6_install.gif
+ :alt: PySide6 Installation GIF
+
+ .. note:: Having Qt installed in your system will not interfere with your
+ PySide6 installation if you do it via ``pip install``, because the Python
+ packages (wheels) already includes Qt binaries. Most notably, style plugins
+ from the system won't have any effect on PySide applications.
+
+
+* **Installing PySide6**
+
+ .. note:: For a commercial installation, refer to :ref:`commercial-page`.
+
+ Now you are ready to install the |project| packages using ``pip``.
+ From the terminal, run the following command:
+
+ * For the latest version::
+
+ pip install pyside6
+
+ * For a specific version, like 6.4.1::
+
+ pip install pyside6==6.4.1
+
+ * It is also possible to install a specific snapshot from our servers.
+ To do so, you can use the following command::
+
+ pip install --index-url=https://download.qt.io/snapshots/ci/pyside/6.4/latest pyside6 --trusted-host download.qt.io
+
+* **Test your installation**
+
+ Now that you have |project| installed, test your setup by running the following Python
+ constructs to print version information::
+
+ import PySide6.QtCore
+
+ # Prints PySide6 version
+ print(PySide6.__version__)
+
+ # Prints the Qt version used to compile PySide6
+ print(PySide6.QtCore.__version__)
+
+ .. note:: For more information about what's included in the ``pyside6``
+ package, check :ref:`package_details`.
+
+
+Create your first Qt Application
+--------------------------------
+
+.. image:: https://qt-wiki-uploads.s3.amazonaws.com/images/e/eb/Pyside6_widgets_quick.gif
+ :alt: Qt Widgets and Qt Quick comparison header animation
+
+Qt provides two technologies to build User Interfaces:
+
+* Qt Widgets, an imperative programming and design approach that has been
+ around since the beginning of Qt, making it a stable and reliable technology
+ for UI applications.
+* Qt Quick, a declarative programming and design approach, which enables you to
+ create fluid UIs by describing them in terms of simple elements.
+
+Both technologies offer you the possibility to use *drag and drop* tools
+to create your interfaces. :ref:`pyside6-designer` for Qt Widgets (included
+when you install pyside6), and Qt Design Studio for Qt Quick (`Get it here`_).
+
+.. note:: After reading this page, it is recommended that you check the
+ :ref:`pyside6-project` tool to learn how to create projects automatically
+ without writing all the code by hand.
+
+.. _`Get it here`: https://doc.qt.io/qt-6/install-qt-design-studio.html
+
+Create your first Qt Application with Qt Widgets
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Your |project| setup is ready. You can explore it further by developing a simple application
+that prints ``"Hello World"`` in several languages. The following instructions will
+guide you through the development process:
+
+* **Imports**
+
+ Create a new file named :code:`hello_world.py`, and add the following imports to it.::
+
+ import sys
+ import random
+ from PySide6 import QtCore, QtWidgets, QtGui
+
+ The |pymodname| Python module provides access to the Qt APIs as its submodule.
+ In this case, you are importing the :ref:`QtCore`, :ref:`QtWidgets`, and :ref:`QtGui` submodules.
+
+* **Main Class**
+
+ Define a class named :code:`MyWidget`, which extends :ref:`QWidget` and
+ includes a :ref:`QPushButton` and :ref:`QLabel`.::
+
+ class MyWidget(QtWidgets.QWidget):
+ def __init__(self):
+ super().__init__()
+
+ self.hello = ["Hallo Welt", "Hei maailma", "Hola Mundo", "Привет мир"]
+
+ self.button = QtWidgets.QPushButton("Click me!")
+ self.text = QtWidgets.QLabel("Hello World",
+ alignment=QtCore.Qt.AlignCenter)
+
+ self.layout = QtWidgets.QVBoxLayout(self)
+ self.layout.addWidget(self.text)
+ self.layout.addWidget(self.button)
+
+ self.button.clicked.connect(self.magic)
+
+ @QtCore.Slot()
+ def magic(self):
+ self.text.setText(random.choice(self.hello))
+
+ The ``MyWidget`` class has the :code:`magic` member function that randomly
+ chooses an item from the :code:`hello` list. When you click the button, the
+ :code:`magic` function is called.
+
+* **Application execution**
+
+ Now, add a main function where you instantiate :code:`MyWidget` and :code:`show` it.::
+
+ if __name__ == "__main__":
+ app = QtWidgets.QApplication([])
+
+ widget = MyWidget()
+ widget.resize(800, 600)
+ widget.show()
+
+ sys.exit(app.exec())
+
+ Run your example by writing the following command:
+
+ .. code-block:: bash
+
+ python hello_world.py
+
+ Try clicking the button at the bottom to see which greeting you get.
+
+ .. image:: images/screenshot_hello_widgets.png
+ :alt: Hello World application in Qt Widgets
+
+Create your first Qt Application with Qt Quick
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To do the same using Qt Quick:
+
+* **Imports**
+
+ Create a new file named :code:`hello_world_quick.py`, and add the following imports to it.::
+
+ import sys
+ from PySide6.QtGui import QGuiApplication
+ from PySide6.QtQml import QQmlApplicationEngine
+
+* **Declarative UI**
+
+ The UI can be described in the QML language:
+
+ .. code-block:: javascript
+
+ import QtQuick
+ import QtQuick.Controls
+ import QtQuick.Layouts
+
+ Window {
+ width: 300
+ height: 200
+ visible: true
+ title: "Hello World"
+
+ readonly property list<string> texts: ["Hallo Welt", "Hei maailma",
+ "Hola Mundo", "Привет мир"]
+
+ function setText() {
+ var i = Math.round(Math.random() * 3)
+ text.text = texts[i]
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ Text {
+ id: text
+ text: "Hello World"
+ Layout.alignment: Qt.AlignHCenter
+ }
+ Button {
+ text: "Click me"
+ Layout.alignment: Qt.AlignHCenter
+ onClicked: setText()
+ }
+ }
+ }
+
+ Put the this into a file named :code:`Main.qml` into a directory named
+ :code:`Example` along with a file named :code:`qmldir` to describe a basic
+ QML module:
+
+ .. code-block:: text
+
+ module Example
+ Main 254.0 Main.qml
+
+* **Application execution**
+
+ Now, add a main function where you instantiate a :ref:`QQmlApplicationEngine` and
+ load the QML::
+
+ import sys
+ from PySide6.QtGui import QGuiApplication
+ from PySide6.QtQml import QQmlApplicationEngine
+
+ if __name__ == "__main__":
+ app = QGuiApplication(sys.argv)
+ engine = QQmlApplicationEngine()
+ engine.addImportPath(sys.path[0])
+ engine.loadFromModule("Example", "Main")
+ if not engine.rootObjects():
+ sys.exit(-1)
+ exit_code = app.exec()
+ del engine
+ sys.exit(exit_code)
+
+ Run your example by writing the following command:
+
+ .. code-block:: bash
+
+ python main.py
+
+
+ Try clicking the button at the bottom to see which greeting you get.
+
+ .. image:: images/screenshot_hello_quick.png
+ :alt: Hello World application in Qt Quick
+
+Next steps
+----------
+
+Now that you have use both technologies, you can head to our
+:ref:`pyside6_examples` and :ref:`pyside6_tutorials` sections.
+
+.. _faq-section:
+
+Frequently Asked Questions
+--------------------------
+
+Here you can find a couple of common questions and situations that will
+clarify questions before you start programming.
+
+.. grid:: 1 3 3 3
+ :gutter: 2
+
+ .. grid-item-card:: What is Qt
+ :link: whatisqt
+ :link-type: ref
+
+ Qt, QML, Widgets... What is the difference?
+
+ .. grid-item-card:: Compatible IDEs
+ :link: whichide
+ :link-type: ref
+
+ Which IDEs are compatible with PySide?
+
+ .. grid-item-card:: Binding Generation
+ :link: whatisshiboken
+ :link-type: ref
+
+ What is Shiboken?
+
+ .. grid-item-card:: File types
+ :link: typesoffiles
+ :link-type: ref
+
+ File Types in PySide
+
+ .. grid-item-card:: App distribution
+ :link: distribution
+ :link-type: ref
+
+ Distributing your application to other systems and platforms
+
+ .. grid-item-card:: Why Qt for Python?
+ :link: whyqtforpython
+ :link-type: ref
+
+ As a Qt/C++ developer, why should I consider Qt for Python?
+
+.. toctree::
+ :hidden:
+
+ faq/whatisqt.rst
+ faq/whichide.rst
+ faq/whatisshiboken.rst
+ faq/typesoffiles.rst
+ faq/distribution.rst
+ faq/whyqtforpython.rst
+ faq/porting_from2.rst
+++ /dev/null
-Getting Started
-===============
-
-.. important:: This page is focused on building |project| **from source**.
- If you just want to install |pymodname|, you need to run: :command:`pip install pyside6`.
-
- For more details, refer to our :ref:`quick-start` guide. Additionally, you can check the
- :ref:`FAQ <faq>` related to the project.
-
-.. _Quick Start: quickstart.html
-
-General Requirements
---------------------
-
-The following prerequisites must be installed before you build |project|.
-On **Linux** you might get them with your operating system package manager, on **macOS**
-you might get them with ``brew``, and on **Windows** you can download the installer from each
-website.
-
-* **Python**: 3.9+ `[official Python website] <https://www.python.org/downloads/>`_
-* **Qt:** 6.4+ `[online installer] <https://download.qt.io/official_releases/online_installers/>`_
-* **CMake:** 3.18+ `[official CMake website] <https://cmake.org/download/>`_
-* **Git:** 2.0+. `[official Git website] <https://git-scm.com/downloads>`_
-* **libclang:** The libclang library, recommended: version 10 for 6.0+.
- Prebuilt versions for each OS can be `downloaded here`_.
-* Check the `Supported Platforms of Qt`_
-
-.. _downloaded here: https://download.qt.io/development_releases/prebuilt/libclang/
-.. _`Supported Platforms of Qt` : https://doc.qt.io/qt-6/supported-platforms.html
-
-Guides per platform
--------------------
-
-.. toctree::
- :maxdepth: 1
- :hidden:
-
- linux.rst
- macOS.rst
- windows.rst
- package_details.rst
- porting_from2.rst
-
-You can refer to the following pages for platform specific instructions:
-
-.. grid:: 1 3 3 3
- :gutter: 2
-
- .. grid-item-card::
- :img-top: ../images/windows.svg
- :class-item: text-center
-
- +++
- .. button-ref:: windows
- :color: primary
- :outline:
- :expand:
-
- Windows
-
- .. grid-item-card::
- :img-top: ../images/macos.svg
- :class-item: text-center
-
- +++
- .. button-ref:: macOS
- :color: primary
- :outline:
- :expand:
-
- macOS
-
- .. grid-item-card::
- :img-top: ../images/linux.svg
- :class-item: text-center
-
- +++
- .. button-ref:: linux
- :color: primary
- :outline:
- :expand:
-
- Linux
-
-.. important:: |project| does not yet support WebAssembly and the mobile operating systems (Android or iOS).
- Most Linux-based embedded OS provide PySide with their official
- package manager (for example, `Raspbian`_ and `ArchlinuxARM`_).
-
-.. _Raspbian: https://www.raspbian.org/
-.. _ArchlinuxARM: https://archlinuxarm.org/
-
-A normal building command will look like this::
-
- python setup.py install --qtpaths=/path/to/qtpaths \
- --ignore-git \
- --debug \
- --build-tests \
- --parallel=8 \
- --verbose-build \
- --module-subset=Core,Gui,Widgets
-
-Which will build and install the project with **debug** symbols, including the **tests**,
-using **ninja** (instead of make), and considering only the **module subset** of
-:mod:`QtCore <PySide6.QtCore>`, :mod:`QtGui <PySide6.QtGui>`, and
-:mod:`QtWidgets <PySide6.QtWidgets>`.
-
-`CMake Unity Build Mode`_ is used by default for speed-up.
-
-Other important options to consider are:
- * ``--cmake``, to specify the path to the cmake binary,
- * ``--reuse-build``, to rebuild only the modified files,
- * ``--openssl=/path/to/openssl/bin``, to use a different path for OpenSSL,
- * ``--standalone``, to copy over the Qt libraries into the final package to make it work on other
- machines,
- * ``--build-docs``, to enable the documentation build.
- * ``--doc-build-online``, to build documentation using the online template (by
- default it uses the offline)
-
-Testing the installation
-------------------------
-
-Once the installation finishes, you will be able to execute any of our examples::
-
- python examples/widgets/widgets/tetrix.py
-
-Running Tests
--------------
-
-Using the ``--build-tests`` option will enable us to run all the auto tests inside the project::
-
- python testrunner.py test > testlog.txt
-
-.. note:: On Windows, don't forget to have qtpaths in your path
- (:command:`set PATH=C:\\\Path\\\to\\\Qt\\\6.4.0\\\msvc2019_64\\\bin;%PATH%`)
-
-You can also run a specific test (for example ``qpainter_test``) by running::
-
- ctest -R qpainter_test --verbose
-
-.. _cross_compilation:
-
-Cross Compilation
------------------
-
-Starting from 6.3, it is possible to cross-compile Shiboken (module), and
-PySide. This functionality is still in Technical Preview, which means it could
-change in the future releases.
-
-.. important:: The only supported configuration is using a host Linux
- machine to cross-compile to a Linux target platform.
-
-Cross compiling software is a valid use case that many projects rely on,
-however, it is a complicated process that might fail due to many reasons.
-
-Before starting with the process, it is important to understand the details of
-the build system, and the goal of cross compilation.
-
-In the build process, a ``Host`` is the computer you are currently using to
-compile, and a ``Target`` is your embedded device that you are compiling for.
-
-Qt for Python is being built using setuptools, and relies on a ``setup.py`` file
-that is called recursively to build Shiboken (module),
-Shiboken (generator), and PySide. As the generator is creating
-the wrappers for the bindings, it's not cross compiled
-for the target.
-Only the Shiboken (module) and PySide are cross compiled.
-
-The building process requires a Qt installation, and a Python interpreter
-on both the host, and the target. The used Qt versions on both platforms
-should have the same minor version. That is, Qt 6.3 (host)
-cannot be used with a Qt 6.2 (target), or the other way around.
-
-.. note:: It is recommended to use the same version of Qt on both host and target to avoid possible
- unconventional errors.
-
-Prerequisites
-~~~~~~~~~~~~~
-
-First and foremost, you need to have access to the target device because you
-need to copy several system files (sysroot). We recommend a Linux OS that has
-the latest Qt versions, like `Manjaro ARM`_ or `Archlinux ARM`_.
-
-* (target) Install Qt 6.3+ on the system using the package manager.
-* (host) Install Qt 6.3+ on the system using the package manager or Qt
- Installer.
-* (target, host) Install the library and development packages that provide
- C++ headers, linkers, libraries, and compilers.
-* (target, host) Install Python interpreter v3.7 or later
-* (target, host) Install CMake 3.17+
-
-After installing these prerequisites, copy the ``target`` sysroot to your
-``host`` computer. This process is tricky, because copying system files from
-another computer might cause problems with the symbolic links. Here you
-have two options to achieve that.
-
-Option A: Copying the files
-***************************
-
-Create a directory to copy the sysroot of your target device,
-for example ``rpi-sysroot``, and perform the copy on your host computer:
-
-.. code-block:: bash
-
- rsync -vR --progress -rl --delete-after --safe-links \
- USERNAME@TARGET_IP:/{lib,usr,opt/vc/lib} rpi-sysroot/
-
-Ensure to replace ``USERNAME`` and ``TARGET_IP`` with your system appropriate
-values.
-
-Option B: Packaging the file system
-***********************************
-
-Create a package for your sysroot in your target:
-
-.. code-block:: bash
-
- tar cfJ ~/sysroot.tar.xz /lib /usr /opt/vc/lib
-
-Copy the package from the target to your host:
-
-.. code-block:: bash
-
- rsync -vR --progress USERNAME@TARGET_IP:sysroot.tar.xz .
-
-Once you have the tar file, unpack it inside a ``rpi-sysroot`` directory.
-
-Fix Symlinks
-************
-
-It is recommended to run the following script to fix
-most of the issues you would find with symbolic links:
-
-.. code-block:: python
-
- import sys
- from pathlib import Path
- import os
-
- # Take a sysroot directory and turn all the absolute symlinks and turn them into
- # relative ones such that the sysroot is usable within another system.
-
- if len(sys.argv) != 2:
- print(f"Usage is {sys.argv[0]} <sysroot-directory>")
- sys.exit(-1)
-
- topdir = Path(sys.argv[1]).absolute()
-
- def handlelink(filep, subdir):
- link = filep.readlink()
- if str(link)[0] != "/":
- return
- if link.startswith(topdir):
- return
- relpath = os.path.relpath((topdir / link).resolve(), subdir)
- os.unlink(filep)
- os.symlink(relpath, filep)
-
- for f in topdir.glob("**/*"):
- if f.is_file() and f.is_symlink():
- handlelink(f, f.parent)
-
-Setting up the toolchain
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-To perform the cross compilation, you need a special set of compilers,
-libraries, and headers, which runs on the host architecture, but generates
-(binaries/executables) for a target architecture.
-For example, from x86_64 to aarch64.
-
-It is recommended to use the official 10.2 `ARM Developer cross compilers`_,
-which you can find on their official website. For this tutorial, we choose
-``aarch64`` target architecture and we will assume that you downloaded the
-`gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu.tar.xz`_ file,
-and unpacked it.
-
-With those compilers, now you need a CMake toolchain file. This is
-a configuration file to set the compilers and sysroot information, together
-with extra options like compilation flags, and other details. You can use the
-following file as an example, but keep in mind they might vary:
-
-.. code-block:: cmake
-
- # toolchain-aarch64.cmake
- cmake_minimum_required(VERSION 3.18)
- include_guard(GLOBAL)
-
- set(CMAKE_SYSTEM_NAME Linux)
- set(CMAKE_SYSTEM_PROCESSOR aarch64)
-
- set(TARGET_SYSROOT /path/to/your/target/sysroot)
- set(CROSS_COMPILER /path/to/your/crosscompiling/compilers/)
-
- set(CMAKE_SYSROOT ${TARGET_SYSROOT})
-
- set(ENV{PKG_CONFIG_PATH} "")
- set(ENV{PKG_CONFIG_LIBDIR} ${CMAKE_SYSROOT}/usr/lib/pkgconfig:${CMAKE_SYSROOT}/usr/share/pkgconfig)
- set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT})
-
- set(CMAKE_C_COMPILER ${CROSS_COMPILER}/aarch64-none-linux-gnu-gcc)
- set(CMAKE_CXX_COMPILER ${CROSS_COMPILER}/aarch64-none-linux-gnu-g++)
-
- set(QT_COMPILER_FLAGS "-march=armv8-a")
- set(QT_COMPILER_FLAGS_RELEASE "-O2 -pipe")
- set(QT_LINKER_FLAGS "-Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed")
-
- set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
- set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
- set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
- set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
-
- include(CMakeInitializeConfigs)
-
- function(cmake_initialize_per_config_variable _PREFIX _DOCSTRING)
- if (_PREFIX MATCHES "CMAKE_(C|CXX|ASM)_FLAGS")
- set(CMAKE_${CMAKE_MATCH_1}_FLAGS_INIT "${QT_COMPILER_FLAGS}")
-
- foreach (config DEBUG RELEASE MINSIZEREL RELWITHDEBINFO)
- if (DEFINED QT_COMPILER_FLAGS_${config})
- set(CMAKE_${CMAKE_MATCH_1}_FLAGS_${config}_INIT "${QT_COMPILER_FLAGS_${config}}")
- endif()
- endforeach()
- endif()
-
- if (_PREFIX MATCHES "CMAKE_(SHARED|MODULE|EXE)_LINKER_FLAGS")
- foreach (config SHARED MODULE EXE)
- set(CMAKE_${config}_LINKER_FLAGS_INIT "${QT_LINKER_FLAGS}")
- endforeach()
- endif()
-
- _cmake_initialize_per_config_variable(${ARGV})
- endfunction()
-
-You need to adjust the paths in these two lines::
-
- set(TARGET_SYSROOT /path/to/your/target/sysroot)
- set(CROSS_COMPILER /path/to/your/crosscompiling/compilers/)
-
-and replace them with the sysroot directory (the one we called ``rpi-sysroot``),
-and the compilers (the ``gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu/bin`` directory).
-
-
-Cross compiling PySide
-~~~~~~~~~~~~~~~~~~~~~~
-
-After you have installed the prerequisites and copied the necessary files, you
-should have the following:
-
-* The compilers to cross compile (``gcc-argm-10.2-...``),
-* The target sysroot (``rpi-sysroot``),
-* The toolchain cmake file (``toolchain-aarch64.cmake``),
-* The ``pyside-setup`` repository,
-
-An example of the ``setup.py`` invocation might look like the following:
-
-.. code-block:: bash
-
- python setup.py bdist_wheel \
- --parallel=8 --ignore-git --reuse-build --standalone --limited-api=yes \
- --cmake-toolchain-file=/opt/toolchain-aarch64.cmake \
- --qt-host-path=/opt/Qt/6.3.0/gcc_64 \
- --plat-name=linux_aarch64
-
-Depending on the target platform, you could use ``linux_armv7``,
-``linux_aarch64``, etc.
-
-For embedded platforms, which typically do not have Qt and its tools fully
-built, the option ``--no-qt-tools`` can be used to prevent the bundling of the
-tools.
-
-If the process succeeds, you will find the target wheels in your ``dist/``
-directory, for example:
-
-.. code-block:: bash
-
- PySide6-6.3.0-6.3.0-cp36-abi3-manylinux2014_aarch64.whl
- shiboken6-6.3.0-6.3.0-cp36-abi3-manylinux2014_aarch64.whl
-
-
-Troubleshooting
-***************
-
-* If the auto-detection mechanism fails to find the Python or Qt installations
- you have in your target device, you can use two additional options::
-
- --python-target-path=...
-
- and::
-
- --qt-target-path=...
-
-* In case the automatic build of the host Shiboken (generator) fails,
- you can specify the custom path using::
-
- --shiboken-host-path=...
-
-.. _`Manjaro ARM`: https://manjaro.org/download/#ARM
-.. _`Archlinux ARM`: https://archlinuxarm.org
-.. _`ARM Developer Cross Compilers`: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-a/downloads
-.. _`gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu.tar.xz`: https://developer.arm.com/-/media/Files/downloads/gnu-a/10.2-2020.11/binrel/gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu.tar.xz
-
-.. _building_documentation:
-
-Building the documentation
---------------------------
-
-Starting from 6.3, the documentation is not being built by default.
-When using the second option described in this section, make sure to use the
-``--build-docs`` option.
-
-Install the specific documentation requirements in your Python virtual
-environment::
-
- pip install -r requirements-doc.txt
-
-You can find the ``requirements-doc.txt`` file in the root of the repository.
-
-Starting from 5.15, there are two options to build the documentation:
-
-1. Building the base documentation (no API)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The process of parsing Qt headers to generate the PySide API documentation can take several
-minutes, this means that modifying a specific section of the documentation we currently have, might
-become a hard task. You may only care about the base documentation, which comprises all the
-documentation except for the API documentation.
-
-To generate this, execute the following command::
-
- python setup.py build_base_docs
-
-This will generate an ``html/`` directory with the following structure::
-
- html
- └── pyside6
- ├── index.html
- ├── ...
- └── shiboken6
- ├── index.html
- └── ...
-
-so you can open the main page ``html/pyside6/index.html`` on your browser to check the generated
-files.
-
-This is useful when updating the general sections of the documentation, adding tutorials,
-modifying the build instructions, and more.
-
-.. note:: In case you are interested in only generating the Example Gallery,
- you would need to run ``python tools/example_gallery/main.py`` to
- generate the examples ``documentation`` for the gallery. This will
- also be used internally by the ``build_base_docs`` target
-
-
-2. Building the documentation (Base + API)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The documentation is being generated using **qdoc** to get the API information, and also **sphinx**
-for the local Python related notes.
-
-The system requires the ``libxml2`` and ``libxslt`` libraries.
-
-After installing ``graphviz`` (included in the ``requirements-doc.txt`` file),
-the ``dot`` command needs to be in PATH, otherwise,
-the process will fail. Installing ``graphviz`` system-wide is also an option.
-
-Since the process relies on a Qt installation, you need to specify where the
-``qtbase`` directory of a Qt source tree is located by passing it to
-the command line option ``--qt-src-dir``.
-
-Once the common ``setup.py`` build process finishes (remember to use
-``--build-docs`` to enable the documentation build, and ``--doc-build-online``
-to get the HTML files), you can go to the generated
-``build/<your_env_name>/build/pyside6`` directory, and run::
-
- ninja apidoc
-
-You can add ``-j X``, to perform the build process in parallel with
-X processes.
-
-.. note:: The :command:`apidoc` make target builds offline documentation in ``QCH``
- (Qt Compressed Help) format by default. You can switch to building for the
- online use with the ``--doc-build-online`` configure option.
-
-The target executes several steps:
-
-#. The ``qdoc`` tool is run over the Qt source code to produce documentation in WebXML format.
-#. ``shiboken6`` is run to extract the functions for which bindings exist from WebXML and convert it into RST.
-#. ``sphinx`` is run to produce the documentation in HTML format.
-
-Re-running the command will not execute step 1 unless the file
-``qdoc-output/webxml/qtcore-index.webxml`` is removed from the build tree.
-Similarly, step 2 will not be executed unless the file ``base/PySide6/QtCore/index.rst``
-is removed.
-
-Finally, you will get a ``html`` directory containing all the generated documentation. The offline
-help files, ``PySide.qch`` and ``Shiboken.qch``, can be moved to any directory of your choice. You
-can find ``Shiboken.qch`` in the build directory, ``build/<your_env_name>/build/shiboken6/doc/html``.
-
-If you want to temporarily change a ``.rst`` file to examine the impact on
-formatting, you can re-run ``sphinx`` in the ``doc`` directory::
-
- sphinx-build base html
-
-Viewing offline documentation
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The offline documentation (QCH) can be viewed using the *Qt Creator* IDE or
-*Qt Assistant*, which is a standalone application for viewing QCH files.
-
-To view the QCH using *Qt Creator*, following the instructions outlined in
-`Using Qt Creator Help Mode <https://doc.qt.io/qtcreator/creator-help.html>`_.
-If you chose to use *Qt Assistant* instead, use the following command to register
-the QCH file before launching *Qt Assistant*::
-
- assistant -register PySide.qch
-
-Troubleshooting documentation
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The documentation uses intersphinx to link from the PySide to the
-Shiboken documentation. This can fail if
-
-* the default ``QCH`` format is used; in which case the required ``objects.inv``
- files are not generated. Use ``--doc-build-online``.
-* base and full doc builds are mixed, resulting in wrong values for the
- intersphinx location in the CMake files. Re-run ``cmake`` to fix this.
-
-Using the internal tools
-------------------------
-
-A set of tools can be found under the ``tools/`` directory inside the ``pyside-setup`` repository.
-
-* ``checklibs.py``: Script to analyze dynamic library dependencies of Mach-O binaries.
- To use this utility, just run::
-
- python checklibs.py /path/to/some.app/Contents/MacOS/Some
-
- This script was fetched from this repository_.
-
-* ``create_changelog.py``: Script used to create the CHANGELOG that you can find in the ``dist/``
- directory. Usage::
-
- python create_changelog.py -r 6.0.1 -v v6.0.0..6.0 -t bug-fix
-
-* ``debug_windows.py``: This script can be used to find out why PySide modules
- fail to load with various DLL errors like Missing DLL or Missing symbol in DLL.
-
- You can think of it as a Windows version of :command:`ldd` / ``LD_DEBUG``.
-
- Underneath, it uses the :command:`cdb.exe` command line debugger and the :command:`gflags.exe`
- tool, which are installed with the latest Windows Kit.
-
- The aim is to help developers debug issues that they may encounter using the PySide imports on
- Windows. The user should then provide the generated log file.
-
- Incidentally it can also be used for any Windows executables, not just Python.
- To use it just run::
-
- python debug_windows.py
-
-* ``missing_bindings.py``: This script is used to compare the state of PySide and PyQt
- regarding available modules and classses. This content is displayed in our `wiki page`_,
- and can be used as follows::
-
- python missing_bindings.py --qt-version 6.0.1 -w all
-
-.. note:: The script relies on BeautifulSoup to parse the content and generate a list of the
- missing bindings.
-
-.. _repository: https://github.com/liyanage/macosx-shell-scripts/
-.. _`wiki page`: https://wiki.qt.io/Qt_for_Python_Missing_Bindings
-.. _BeautifulSoup: https://www.crummy.com/software/BeautifulSoup/
-.. _`CMake Unity Build Mode` : https://cmake.org/cmake/help/latest/prop_tgt/UNITY_BUILD.html
+++ /dev/null
-Getting Started on Linux
-==========================
-
-Requirements
-------------
-
-* GCC
-* ``sphinx`` package for the documentation (optional).
-* Depending on your linux distribution, the following dependencies might also be required:
-
- * ``libgl-dev``, ``python-dev``, and ``python-setuptools``.
-* Check the platform dependencies of `Qt for Linux/X11`_.
-
-Building from source
---------------------
-
-Creating a virtual environment
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The ``venv`` module allows you to create a local, user-writeable copy of a python environment into
-which arbitrary modules can be installed and which can be removed after use::
-
- python -m venv testenv
- source testenv/bin/activate
-
-will create and use a new virtual environment, which is indicated by the command prompt changing.
-
-Setting up CLANG
-~~~~~~~~~~~~~~~~
-
-If you don't have libclang already in your system, you can download from the Qt servers::
-
- wget https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_140-based-linux-Rhel8.2-gcc9.2-x86_64.7z
-
-Extract the files, and leave it on any desired path, and set the environment
-variable required::
-
- 7z x libclang-release_140-based-linux-Rhel8.2-gcc9.2-x86_64.7z
- export LLVM_INSTALL_DIR=$PWD/libclang
-
-Getting the source
-~~~~~~~~~~~~~~~~~~
-
-Cloning the official repository can be done by::
-
- git clone https://code.qt.io/pyside/pyside-setup
-
-Checking out the version that we want to build, for example 6.5::
-
- cd pyside-setup && git checkout 6.5
-
-Install the general dependencies::
-
- pip install -r requirements.txt
-
-.. note:: Keep in mind you need to use the same version as your Qt installation.
- Additionally, :command:`git checkout -b 6.5 --track origin/6.5` could be a better option
- in case you want to work on it.
-
-Building and Installing (setuptools)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The ``setuptools`` approach uses the ``setup.py`` file to execute the build,
-install, and packaging steps.
-
-Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
-for example, :command:`/opt/Qt/6.5.0/gcc_64/bin/qtpaths`.
-
-Build can take a few minutes, so it is recommended to use more than one CPU core::
-
- python setup.py build --qtpaths=/opt/Qt/6.5.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
-
-To install on the current directory, just run::
-
- python setup.py install --qtpaths=/opt/Qt/6.5.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
-
-Building and Installing (cmake)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The ``setuptools`` approach includes internal ``CMake`` calls when
-building and installing the project, but a CMake-only approach is only
-recommended for packaging the project for distribution builds.
-
-Assumming that Qt is in PATH, for example, the configure step can be done with::
-
- cmake -B /path/to/the/build/directory \
- -S /path/to/the/pyside-setup \
- -DCMAKE_INSTALL_PREFIX=/where/to/install \
- -DPython_EXECUTABLE=/path/to/interpreter
-
-.. note:: You can add `-DFORCE_LIMITED_API=yes` in case you want to have a
- build which will be compatible with Python 3.7+.
-
-and then for building::
-
- cmake --build /path/to/the/build/directory --parallel X
-
-where `X` is the amount of processes you want to use.
-Finally, the install step can be done with::
-
- cmake --install /path/to/the/build/directory
-
-.. note:: You can build only pyside6 or only shiboken6 by using
- the diferent source directories with the option `-S`.
-
-
-Test installation
-~~~~~~~~~~~~~~~~~
-
-You can execute one of the examples to verify the process is properly working.
-Remember to properly set the environment variables for Qt and PySide::
-
- python examples/widgets/widgets/tetrix.py
-
-.. _`Qt for Linux/X11`: https://doc.qt.io/qt-6/linux.html
+++ /dev/null
-Getting Started on macOS
-========================
-
-Requirements
-------------
-
-* `XCode`_ 8.2 (macOS 10.11), 8.3.3 (macOS 10.12), 9 (macOS 10.13), 10.1 (macOS 10.14)
-* ``sphinx`` package for the documentation (optional).
-* Depending on your OS, the following dependencies might also be required:
-
- * ``libgl-dev``, ``python-dev``, and ``python-setuptools``.
-
-* Check the platform dependencies of `Qt for macOS`_.
-
-.. _XCode: https://developer.apple.com/xcode/
-.. _`Qt for macOS`: https://doc.qt.io/qt-6/macos.html
-
-Building from source
---------------------
-
-Creating a virtual environment
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The ``venv`` module allows you to create a local, user-writeable copy of a python environment into
-which arbitrary modules can be installed and which can be removed after use::
-
- python -m venv testenv # your interpreter could be called 'python3'
- source testenv/bin/activate
-
-will create and use a new virtual environment, which is indicated by the command prompt changing.
-
-Setting up CLANG
-~~~~~~~~~~~~~~~~
-
-If you don't have libclang already in your system, you can download from the Qt servers::
-
- wget https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_140-based-macos-universal.7z
-
-Extract the files, and leave it on any desired path, and set the environment
-variable required::
-
- 7z x libclang-release_140-based-macos-universal.7z
- export LLVM_INSTALL_DIR=$PWD/libclang
-
-Getting PySide
-~~~~~~~~~~~~~~
-
-Cloning the official repository can be done by::
-
- git clone https://code.qt.io/pyside/pyside-setup
-
-Checking out the version that we want to build, for example, 6.5::
-
- cd pyside-setup && git checkout 6.5
-
-Install the general dependencies::
-
- pip install -r requirements.txt
-
-.. note:: Keep in mind you need to use the same version as your Qt installation
-
-Building PySide
-~~~~~~~~~~~~~~~
-
-Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
-for example, ``/opt/Qt/6.5.0/gcc_64/bin/qtpaths``.
-
-Build can take a few minutes, so it is recommended to use more than one CPU core::
-
- python setup.py build --qtpaths=/opt/Qt/6.5.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
-
-Installing PySide
-~~~~~~~~~~~~~~~~~
-
-To install on the current directory, just run::
-
- python setup.py install --qtpaths=/opt/Qt/6.5.0/gcc_64/bin/qtpaths --build-tests --ignore-git --parallel=8
-
-Test installation
-~~~~~~~~~~~~~~~~~
-
-You can execute one of the examples to verify the process is properly working.
-Remember to properly set the environment variables for Qt and PySide::
-
- python examples/widgets/widgets/tetrix.py
+++ /dev/null
-.. _package_details:
-
-Package Details
-===============
-
-Having a large project as the Qt Framework available from one simple
-installation line::
-
- pip install pyside6
-
-is really beneficial,
-but it might be confusing to newcomers.
-
-Besides your IDE, you don't need to install anything else to develop your
-Qt application, because the same command installs many tools
-that will help you design UIs, use QML types, generate
-files automatically, translate applications, etc.
-
-Package Dependencies
---------------------
-
-.. image:: packages.png
- :width: 400
- :alt: Packages structure and dependency
-
-Starting from 6.3.0, the ``pyside6`` package (wheel) is almost empty,
-and only includes references to other packages that are required
-to properly use all the modules.
-This packages are:
-
-* ``pyside6-essentials``, `essential Qt modules <https://pypi.org/project/PySide6-Essentials/>`_,
-* ``pyside6-addons``, `additional Qt modules <https://pypi.org/project/PySide6-Addons/>`_,
-* ``shiboken6``, a utility Python module.
-
-You can verify this by running ``pip list`` to check the installed
-packages in your Python (virtual) environment::
-
- (env) % pip list
- Package Version
- ------------------ -------
- pip 22.0.4
- PySide6 6.3.0
- PySide6-Addons 6.3.0
- PySide6-Essentials 6.3.0
- setuptools 58.1.0
- shiboken6 6.3.0
-
-Both ``pyside6-essentials`` and ``pyside6-addons`` contain Qt binaries
-(``.so``, ``.dll``, or ``.dylib``) that are used by the Python wrappers
-that enable you to use the Qt modules from Python.
-For example, in the ``QtCore`` module, you will find
-on Linux:
-
-* ``PySide6/QtCore.abi3.so``, and
-* ``PySide6/Qt/lib/libQt6Core.so.6``
-
-inside the ``site-packages`` directory of your (virtual) environment.
-The first is the *importable* module which depends on the second file
-which is the original QtCore library.
-
-.. note:: The package ``shiboken6-generator`` is not a dependency,
- and it's not available on PyPi. The reason, is that it depends on
- ``libclang``, which is a large library that we don't package, and
- requires a special configuration for you to use. Check the `Shiboken
- Documentation`_ for more details.
-
-..
- Adding the full URL because it's a different sphinx project.
-.. _`Shiboken Documentation`: https://doc.qt.io/qtforpython/shiboken6/gettingstarted.html
-
-Tools Included
---------------
-
-PySide6 comes bundled with a set of tools that assist in making the development experience with
-PySide6 more efficient. The list of tools can be found :ref:`here <package_tools>`.
+++ /dev/null
-Porting Applications from PySide2 to PySide6
-============================================
-
-Module Availability
--------------------
-
-Qt for Python 6.2.0 provides all modules planned for inclusion in Qt 6.
-
-Module-Level Changes
---------------------
-
-* The modules *QtMacExtras*, *Qt Quick Controls 1*, *QtWinExtras*,
- *QtXmlPatterns* and *QtX11Extras* have been removed.
-* ``QStateMachine`` and related classes have been extracted to a new
- *QtStateMachine* module.
-* The modules *QtWebKit* and *QtWebKitWidgets* have been replaced by the new
- *QtWebEngineCore*, *QtWebEngineQuick* and *QtWebEngineWidgets* modules.
-* ``QXmlReader`` and related classes (*SAX API*) have been removed.
-* The content of the *QtOpenGL* module has been replaced. The class
- ``QGLWidget`` and related classes (``QGLContext``, ``QGLFunctions``,
- ``QGLShaderProgram``) have been removed. Parts of the *Open GL*
- functionality from *QtGui* have been extracted into this module, for example
- ``QOpenGLBuffer`` and ``QOpenGLShaderProgram``.
- There is a new module *QtOpenGLWidgets* which contains the class
- ``QOpenGLWidget``, a replacement for ``QGLWidget``.
-
-As *Open GL* is phasing out,
-`QRhi <https://doc.qt.io/qt-6/topics-graphics.html>`_ should be considered
-for graphics applications.
-
-Imports
--------
-
-The first thing to do when porting applications is to replace the
-import statements:
-
-.. code-block:: python
-
- from PySide2.QtWidgets import QApplication
- from PySide2 import QtCore
-
-needs to be changed to:
-
-.. code-block:: python
-
- from PySide6.QtWidgets import QApplication
- from PySide6 import QtCore
-
-
-Some classes are in a different module now, for example
-``QAction`` and ``QShortcut`` have been moved from ``QtWidgets`` to ``QtGui``.
-
-For *Qt Charts* and *Qt Data Visualization*, the additional namespaces have been
-removed. It is now possible to use:
-
-.. code-block:: python
-
- from PySide6.QtCharts import QChartView
-
-directly.
-
-
-Class/Function Deprecations
----------------------------
-
-Then, the code base needs to be checked for usage of deprecated API and adapted
-accordingly. For example:
-
-* The High DPI scaling attributes ``Qt.AA_EnableHighDpiScaling``,
- ``Qt.AA_DisableHighDpiScaling`` and ``Qt.AA_UseHighDpiPixmaps`` are
- deprecated. High DPI is by default enabled in Qt 6 and cannot be turned off.
-* ``QDesktopWidget`` has been removed. ``QScreen`` should be used instead,
- which can be retrieved using ``QWidget.screen()``,
- ``QGuiApplication.primaryScreen()`` or ``QGuiApplication.screens()``.
-* ``QFontMetrics.width()`` has been renamed to ``horizontalAdvance()``.
-* ``QMouseEvent.pos()`` and ``QMouseEvent.globalPos()`` returning a ``QPoint``
- as well as ``QMouseEvent.x()`` and ``QMouseEvent.y()`` returning ``int``
- are now deprecated. ``QMouseEvent.position()`` and
- ``QMouseEvent.globalPosition()`` returning a ``QPointF`` should be used
- instead.
-* ``Qt.MidButton`` has been renamed to ``Qt.MiddleButton``.
-* ``QOpenGLVersionFunctionsFactory.get()`` instead of
- ``QOpenGLContext.versionFunctions()`` should be used to obtain
- *Open GL* functions.
-* ``QRegExp`` has been replaced by ``QRegularExpression``.
-* ``QWidget.mapToGlobal()`` and ``QWidget.mapFromGlobal()`` now also accept
- and return ``QPointF``.
-* Functions named ``exec_`` (classes ``QCoreApplication``, ``QDialog``,
- ``QEventLoop``) have been renamed to ``exec`` which became possible
- in Python 3.
-
-More information can be found in the
-`Porting to Qt 6 <https://doc.qt.io/qt-6/portingguide.html>`_ Guide
-and the `Qt 6.2 Documentation <https://doc.qt.io/qt-6/index.html>`_ .
+++ /dev/null
-Getting Started on Windows
-==========================
-
-The Qt library has to be built with the same version of MSVC as Python and PySide, this can be
-selected when using the online installer.
-
-Requirements
-------------
-
-* `MSVC2022`_ or (MSVC2019) for Python 3 on Windows,
-* `OpenSSL`_ (optional for SSL support, Qt must have been configured using the same SSL library).
-* ``sphinx`` package for the documentation (optional).
-* Check the platform dependencies of `Qt for Windows`_.
-
-.. note:: The Python provided by the Microsoft Store is not compatible with PySide. Please
- use https://www.python.org/downloads/ to get a Python Interpreter.
-
-.. _MSVC2022: https://visualstudio.microsoft.com/downloads/
-.. _OpenSSL: https://sourceforge.net/projects/openssl/
-.. _`Qt for Windows`: https://doc.qt.io/qt-6/windows.html
-
-Building from source on Windows 10
-----------------------------------
-
-Creating a virtual environment
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The ``venv`` module allows you to create a local, user-writeable copy of a python environment into
-which arbitrary modules can be installed and which can be removed after use::
-
- python -m venv testenv
- call testenv\Scripts\activate
-
-will create and use a new virtual environment, which is indicated by the command prompt changing.
-
-Setting up CLANG
-~~~~~~~~~~~~~~~~
-
-libclang can be downloaded from the
-`Qt servers <https://download.qt.io/development_releases/prebuilt/libclang>`_.
-for example, ``libclang-release_140-based-windows-vs2019_64.7z``.
-
-Note that from version 12 onwards, the prebuilt Windows binaries from
-`LLVM <https://www.llvm.org>`_ no longer contain CMake configuration files; so
-they can no longer be used.
-
-Extract the files, and leave it on any desired path, for example, ``c:``,
-and set the environment variable required::
-
- set LLVM_INSTALL_DIR=c:\libclang
- set PATH=C:\libclang\bin;%PATH%
-
-Getting PySide
-~~~~~~~~~~~~~~
-
-Cloning the official repository can be done by::
-
- git clone https://code.qt.io/pyside/pyside-setup
-
-Checking out the version that we want to build, for example, 6.5::
-
- cd pyside-setup && git checkout 6.5
-
-Install the general dependencies::
-
- pip install -r requirements.txt
-
-.. note:: Keep in mind you need to use the same version as your Qt installation
-
-Building PySide
-~~~~~~~~~~~~~~~
-
-Check your Qt installation path, to specifically use that version of qtpaths to build PySide.
-for example, ``C:\Qt\6.5.0\msvc2019_64\bin\qtpaths.exe``.
-
-Build can take a few minutes, so it is recommended to use more than one CPU core::
-
- python setup.py build --qtpaths=c:\path\to\qtpaths.exe --openssl=c:\path\to\openssl\bin --build-tests --ignore-git --parallel=8
-
-.. _creating_windows_debug_builds:
-
-Creating Debug Builds
-~~~~~~~~~~~~~~~~~~~~~
-
-* Choose *Custom Installation* when installing Python and tick the options for
- debug binaries and libraries
-
-* Use ``venv`` to create a virtual environment and pass the debug binary::
-
- python_d.exe -m venv testenv_d
-
-* Use ``python_d.exe`` to invoke ``setup.py``
-
-.. note:: Make sure you add the ``--debug`` option to the ``python setup.py install`` to produce a debug build
-
-
-Installing PySide
-~~~~~~~~~~~~~~~~~
-
-To install on the current directory, just run::
-
- python setup.py install --qtpaths=c:\path\to\qtpaths.exe --openssl=c:\path\to\openssl\bin --build-tests --ignore-git --parallel=8
-
-Test installation
-~~~~~~~~~~~~~~~~~
-
-You can execute one of the examples to verify the process is properly working.
-Remember to properly set the environment variables for Qt and PySide::
-
- python examples/widgets/widgets/tetrix.py
which enables you to use Python to write your Qt applications.
The project has two main components:
- * `PySide6`_, so that you can use Qt6 APIs in your Python applications, and
+ * :ref:`PySide6 <getting-started>`, so that you can use Qt6 APIs in your Python applications, and
* `Shiboken6 <shiboken6/index.html>`__, a binding generator tool, which can
be used to expose C++ projects to Python, and a Python module with
some utility functions.
which enables you to use Python to write your Qt applications.
The project has two main components:
- * `PySide6`_, so that you can use Qt6 APIs in your Python applications, and
+ * :ref:`PySide6 <getting-started>`, so that you can use Qt6 APIs in your Python applications, and
* `Shiboken6 <../shiboken6/index.html>`__, a binding generator tool, which can
be used to expose C++ projects to Python, and a Python module with
some utility functions.
-`Porting from PySide2 to PySide6`_ provides information on porting existing PySide2
-applications.
+:ref:`Porting from PySide2 to PySide6 <faq-porting-from2>` provides information on porting existing
+PySide2 applications.
This project is available under the LGPLv3/GPLv3 and the `Qt commercial license`_.
.. _Qt: https://doc.qt.io
-.. _PySide6: quickstart.html
-.. _`Qt commercial license`: https://www.qt.io/licensing/
-.. _`Porting from PySide2 to PySide6`: gettingstarted/porting_from2.html
+.. _`Qt commercial license`: https://www.qt.io/licensing/`
Quick Start
===========
You can obtain the latest stable version by running ``pip install pyside6``.
-If you want to build it yourself, check the `getting started guide`_.
+If you want to build it yourself, check the :ref:`Building from Source <buildingfromsource>` guide.
-To learn how to use it, check out `write your first application`_,
+To learn how to use it, check out :ref:`write your first application <gettingstarted>`,
and to learn what is installed with the ``pyside6``, check the
-`package content, structure, and tools`_ page.
-
-
-.. _`getting started guide`: gettingstarted/index.html
-.. _`write your first application`: quickstart.html
-.. _`package content, structure, and tools`: gettingstarted/package_details.html
+:ref:`package content, structure, and tools <package_details>` page.
Documentation
=============
Write your first Qt application.
+++
- .. button-ref:: quick-start
+ .. button-ref:: getting-started
:color: primary
:outline:
:expand:
:img-top: images/Support.svg
:class-item: text-center
- Modules, docs, and cross compilation.
+ Building from Source.
+++
- .. button-ref:: gettingstarted/index
+ .. button-ref:: building_from_source/index
:color: primary
:outline:
:expand:
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import sys
return result
-class InheritanceGraph(object):
+class InheritanceGraph:
"""
Given a list of classes, determines the set of classes that they inherit
from all the way to the root "object", and then is able to generate a
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import json
import os
.. grid:: 1 3 3 3
:gutter: 2
- .. grid-item-card:: :mod:`QtBluetooth <PySide6.QtBluetooth>`
+ .. grid-item-card:: :mod:`Qt Bluetooth <PySide6.QtBluetooth>`
- The Bluetooth API provides connectivity between Bluetooth enabled
- devices.
+ Provides access to Bluetooth hardware.
+ .. grid-item-card:: :mod:`Qt Charts <PySide6.QtCharts>`
- .. grid-item-card:: :mod:`QtCharts <PySide6.QtCharts>`
+ UI Components for displaying visually pleasing charts,
+ driven by static or dynamic data models.
- Provides a set of easy to use chart components.
+ .. grid-item-card:: :mod:`Qt Concurrent <PySide6.QtConcurrent>`
- .. grid-item-card:: :mod:`QtConcurrent <PySide6.QtConcurrent>`
+ Classes for writing multi-threaded programs without using low-level
+ threading primitives.
- Provides high-level APIs that make it possible to write multi-threaded
- programs without using low-level threading primitives such as mutexes,
- read-write locks, wait conditions, or semaphores.
+ .. grid-item-card:: :mod:`Qt Core <PySide6.QtCore>`
+ Core non-graphical classes used by other modules.
- .. grid-item-card:: :mod:`QtCore <PySide6.QtCore>`
+ .. grid-item-card:: :mod:`Qt Data Visualization <PySide6.QtDataVisualization>`
- Provides core non-GUI functionality.
+ UI Components for creating stunning 3D data visualizations.
- .. grid-item-card:: :mod:`QtDataVisualization <PySide6.QtDataVisualization>`
+ .. grid-item-card:: :mod:`Qt D-Bus <PySide6.QtDBus>`
- Provides a way to visualize data in 3D as bar, scatter, and surface
- graphs.
+ Classes for inter-process communication over the D-Bus protocol.
- .. grid-item-card:: :mod:`QtDBus <PySide6.QtDBus>`
+ .. grid-item-card:: :mod:`Qt Designer <PySide6.QtDesigner>`
- D-Bus is an Inter-Process Communication (IPC) and Remote Procedure
- Calling (RPC) mechanism originally developed for Linux to replace
- existing and competing IPC solutions with one unified protocol
+ Provides classes to extend *Qt Widgets Designer*.
- .. grid-item-card:: :mod:`QtDesigner <PySide6.QtDesigner>`
+ .. grid-item-card:: :mod:`Qt GUI <PySide6.QtGui>`
- Provides classes to extend *Qt Widgets Designer*.
+ Base classes for graphical user interface (GUI) components.
+
+ .. grid-item-card:: :mod:`Qt Graphs <PySide6.QtGraphs>`
+
+ Provides functionality for visualizing data in 3D as bar, scatter, and
+ surface graphs, as well as 2D in area, bar, donut, line, pie, scatter, and
+ spline graphs.
+
+ .. grid-item-card:: :mod:`QtGraphs Widgets <PySide6.QtGraphsWidgets>`
+
+ Provides the widget-based graphs API.
+
+ .. grid-item-card:: :mod:`Qt Help <PySide6.QtHelp>`
- .. grid-item-card:: :mod:`QtGui <PySide6.QtGui>`
+ Classes for integrating documentation into applications.
- Extends QtCore with GUI functionality.
+ .. grid-item-card:: :mod:`Qt HTTP Server <PySide6.QtHttpServer>`
- .. grid-item-card:: :mod:`QtHelp <PySide6.QtHelp>`
+ A framework for embedding an HTTP server into a Qt application.
- Provides classes for integrating online documentation in applications.
+ .. grid-item-card:: :mod:`Qt Location <PySide6.QtLocation>`
+
+ Provides QML and Python interfaces to create location-aware applications.
.. grid-item-card:: :mod:`Qt Multimedia <PySide6.QtMultimedia>`
- Provides API for multimedia-specific use cases.
+ A rich set of QML types and Python classes to handle multimedia content.
+ Also includes APIs to handle camera access.
.. grid-item-card:: :mod:`Qt Multimedia Widgets <PySide6.QtMultimediaWidgets>`
Provides the widget-based multimedia API.
- .. grid-item-card:: :mod:`QtNetwork <PySide6.QtNetwork>`
+ .. grid-item-card:: :mod:`Qt Network <PySide6.QtNetwork>`
- Offers classes that let you to write TCP/IP clients and servers.
+ Classes to make network programming easier and more portable.
.. grid-item-card:: :mod:`Qt Network Authorization <PySide6.QtNetworkAuth>`
- Provides a set of APIs that enable Qt applications to obtain limited
- access to online accounts and HTTP services without exposing users'
- passwords.
+ Provides support for OAuth-based authorization to online services.
- .. grid-item-card:: :mod:`QtNfc <PySide6.QtNfc>`
+ .. grid-item-card:: :mod:`Qt NFC <PySide6.QtNfc>`
- The NFC API provides connectivity between NFC enabled devices.
+ Provides access to Near-Field communication (NFC) hardware. On desktop
+ platforms NDEF access is only supported for Type 4 tags.
- .. grid-item-card:: :mod:`QtOpenGL <PySide6.QtOpenGL>`
+ .. grid-item-card:: :mod:`Qt OpenGL <PySide6.QtOpenGL>`
- Offers classes that make it easy to use OpenGL in Qt applications.
+ Classes that make it easy to use OpenGL in Qt applications.
- .. grid-item-card:: :mod:`QtOpenGL Widgets <PySide6.QtOpenGLWidgets>`
+ .. grid-item-card:: :mod:`Qt OpenGL Widgets <PySide6.QtOpenGLWidgets>`
- Provides the OpenGLWidget class enabling OpenGL rendering for
- a certain part of the widget tree.
+ Provides a widget for rendering OpenGL graphics.
.. grid-item-card:: :mod:`Qt Positioning <PySide6.QtPositioning>`
- Provides access to position, satellite info and area monitoring
- classes.
+ Provides access to position, satellite info and area monitoring classes.
.. grid-item-card:: :mod:`Qt PDF <PySide6.QtPdf>`
- Classes and functions for rendering PDF documents.
+ Classes and functions for rendering PDF documents on desktop platforms.
.. grid-item-card:: :mod:`Qt PDF Widgets <PySide6.QtPdfWidgets>`
A PDF viewer widget.
- .. grid-item-card:: :mod:`QtPrintSupport <PySide6.QtPrintSupport>`
+ .. grid-item-card:: :mod:`Qt Print Support <PySide6.QtPrintSupport>`
+
+ Classes to make printing easier and more portable.
- Provides extensive cross-platform support for printing.
+ .. grid-item-card:: :mod:`Qt Qml <PySide6.QtQml>`
- .. grid-item-card:: :mod:`QtQml <PySide6.QtQml>`
+ Classes for QML and JavaScript languages.
- Python API for Qt QML.
+ .. grid-item-card:: :mod:`Qt Quick <PySide6.QtQuick>`
- .. grid-item-card:: :mod:`QtQuick <PySide6.QtQuick>`
+ A declarative framework for building highly dynamic applications
+ with custom UIs.
- Provides classes for embedding Qt Quick in Qt applications.
+ .. grid-item-card:: :mod:`Qt Quick 3D <PySide6.QtQuick3D>`
- .. grid-item-card:: :mod:`QtQuickControls2 <PySide6.QtQuickControls2>`
+ Provides a high-level API for creating 3D content or UIs based on Qt Quick.
- Provides classes for setting up the controls from C++.
+ .. grid-item-card:: :mod:`Qt Quick Controls <PySide6.QtQuickControls2>`
- .. grid-item-card:: :mod:`QtQuickTest <PySide6.QtQuickTest>`
+ Lightweight QML types for creating performant user interfaces for
+ desktop, embedded, and mobile devices.
- A unit test framework for QML applications where test cases are written as JavaScript functions.
+ .. grid-item-card:: :mod:`Qt Quick Test <PySide6.QtQuickTest>`
- .. grid-item-card:: :mod:`QtQuickWidgets <PySide6.QtQuickWidgets>`
+ A unit test framework for QML applications where test cases are written as JavaScript functions.
- Provides the QQuickWidget class for embedding Qt Quick in widget-based applications.
+ .. grid-item-card:: :mod:`Qt Quick Widgets <PySide6.QtQuickWidgets>`
- .. grid-item-card:: :mod:`QtRemoteObjects <PySide6.QtRemoteObjects>`
+ Provides a Python widget class for displaying a Qt Quick user interface.
- Inter-Process Communication (IPC) module developed for Qt. This module
- extends Qt's existing functionalities to enable information exchange
- between processes or computers, easily.
+ .. grid-item-card:: :mod:`Qt Remote Objects <PySide6.QtRemoteObjects>`
- .. grid-item-card:: :mod:`Qt Scxml <PySide6.QtScxml>`
+ Provides an easy to use mechanism for sharing a QObject's API
+ (Properties/Signals/Slots) between processes or devices.
- Provides classes to create and use state machines from SCXML files.
+ .. grid-item-card:: :mod:`Qt SCXML <PySide6.QtScxml>`
+
+ Provides classes and tools for creating state machines from SCXML files and
+ embedding them in applications.
.. grid-item-card:: :mod:`Qt Sensors <PySide6.QtSensors>`
.. grid-item-card:: :mod:`Qt Spatial Audio <PySide6.QtSpatialAudio>`
- Provides APIs for modeling sound source and their surrounds in 3D
- space.
+ Provides support for spatial audio. Create sound scenes in 3D space containing
+ different sound sources and room related properties such as reverb.
- .. grid-item-card:: :mod:`QtSql <PySide6.QtSql>`
+ .. grid-item-card:: :mod:`Qt SQL <PySide6.QtSql>`
- Helps you provide seamless database integration to your Qt
- applications.
+ Classes for database integration using SQL.
- .. grid-item-card:: :mod:`QtStateMachine <PySide6.QtStateMachine>`
+ .. grid-item-card:: :mod:`Qt State Machine <PySide6.QtStateMachine>`
Provides classes for creating and executing state graphs.
- .. grid-item-card:: :mod:`QtSvg <PySide6.QtSvg>`
+ .. grid-item-card:: :mod:`Qt SVG <PySide6.QtSvg>`
- Provides classes for displaying the contents of SVG files.
+ Classes for displaying the contents of SVG files. Supports a subset of the
+ SVG 1.2 Tiny standard. A separate library (Qt SVG Widgets) provides support
+ for rendering SVG files in a widget UI.
- .. grid-item-card:: :mod:`QtSvgWidgets <PySide6.QtSvgWidgets>`
+ .. grid-item-card:: :mod:`Qt SVG Widgets <PySide6.QtSvgWidgets>`
- Provides a widget that is used to display the contents of SVG files.
+ Provides support for rendering SVG files in a widget UI.
- .. grid-item-card:: :mod:`QtTest <PySide6.QtTest>`
+ .. grid-item-card:: :mod:`Qt Test <PySide6.QtTest>`
Provides classes for unit testing Qt applications and libraries.
- .. grid-item-card:: :mod:`QtUiTools <PySide6.QtUiTools>`
+ .. grid-item-card:: :mod:`QtTextToSpeech <PySide6.QtTextToSpeech>`
+
+ Provides support for synthesizing speech from text and playing it as audio
+ output.
- Provides classes to handle forms created with *Qt Widgets Designer*.
+ .. grid-item-card:: :mod:`Qt UI Tools <PySide6.QtUiTools>`
+
+ Classes for loading QWidget based forms created in *Qt Widgets Designer*
+ dynamically, at runtime.
.. grid-item-card:: :mod:`Qt WebChannel <PySide6.QtWebChannel>`
Provides access to QObject or QML objects from HTML clients for
seamless integration of Qt applications with HTML/JavaScript clients.
- .. grid-item-card:: :mod:`QtWebEngine Core C++ Classes <PySide6.QtWebEngineCore>`
+ .. grid-item-card:: :mod:`QtWebEngine Core Classes <PySide6.QtWebEngineCore>`
Provides public API shared by both QtWebEngine and QtWebEngineWidgets.
- .. grid-item-card:: :mod:`QtWebEngine Widgets C++ Classes <PySide6.QtWebEngineWidgets>`
+ .. grid-item-card:: :mod:`QtWebEngine Widgets Classes <PySide6.QtWebEngineWidgets>`
- Provides C++ classes for rendering web content in a QWidget based
+ Provides Python classes for rendering web content in a QWidget based
application.
.. grid-item-card:: :mod:`QtWebEngine QML Types <PySide6.QtWebEngineQuick>`
Provides WebSocket communication compliant with RFC 6455.
- .. grid-item-card:: :mod:`QtWidgets <PySide6.QtWidgets>`
+ .. grid-item-card:: :mod:`Qt WebView <PySide6.QtWebView>`
+
+ Displays web content in a QML application by using APIs native to the platform,
+ without the need to include a full web browser stack.
+
+ .. grid-item-card:: :mod:`Qt Widgets <PySide6.QtWidgets>`
- Extends Qt GUI with C++ widget functionality.
+ Classes to extend Qt GUI with Python widgets.
- .. grid-item-card:: :mod:`QtXml <PySide6.QtXml>`
+ .. grid-item-card:: :mod:`Qt XML <PySide6.QtXml>`
- Provides a C++ implementation of DOM.
+ Handling of XML in a Document Object Model (DOM) API.
- .. grid-item-card:: :mod:`Qt3DAnimation <PySide6.Qt3DAnimation>`
+ .. grid-item-card:: :mod:`Qt 3D Animation Classes <PySide6.Qt3DAnimation>`
- Provides basic elements required to animate 3D objects.
+ The Qt 3D Animation modules provides a set of prebuilt elements to
+ help you get started with Qt 3D.
- .. grid-item-card:: :mod:`Qt3D Core <PySide6.Qt3DCore>`
+ .. grid-item-card:: :mod:`Qt 3D Core Classes <PySide6.Qt3DCore>`
- Contains functionality to support near-realtime simulation systems.
+ The Qt 3D module contains functionality to support near-realtime
+ simulation systems.
- .. grid-item-card:: :mod:`Qt3D Extras <PySide6.Qt3DExtras>`
+ .. grid-item-card:: :mod:`Qt 3D Extras Classes <PySide6.Qt3DExtras>`
Provides a set of prebuilt elements to help you get started with Qt 3D.
- .. grid-item-card:: :mod:`Qt3D Input <PySide6.Qt3DInput>`
+ .. grid-item-card:: :mod:`Qt 3D Input Classes<PySide6.Qt3DInput>`
- Provides classes for handling user input in applications using Qt 3D.
+ Provides classes for handling user input in
+ applications using Qt3D.
- .. grid-item-card:: :mod:`Qt3D Logic <PySide6.Qt3DLogic>`
+ .. grid-item-card:: :mod:`Qt 3D Logic Classes <PySide6.Qt3DLogic>`
Enables synchronizing frames with the Qt 3D backend.
- .. grid-item-card:: :mod:`Qt3D Render <PySide6.Qt3DRender>`
+ .. grid-item-card:: :mod:`Qt 3D Render Classes <PySide6.Qt3DRender>`
Contains functionality to support 2D and 3D rendering using Qt 3D.
--- /dev/null
+.. _package_details:
+
+Package Details
+===============
+
+Having a large project as the Qt Framework available from one simple
+installation line::
+
+ pip install pyside6
+
+is really beneficial,
+but it might be confusing to newcomers.
+
+Besides your IDE, you don't need to install anything else to develop your
+Qt application, because the same command installs many tools
+that will help you design UIs, use QML types, generate
+files automatically, translate applications, etc.
+
+Package Dependencies
+--------------------
+
+.. image:: packages.png
+ :width: 400
+ :alt: Packages structure and dependency
+
+Starting from 6.3.0, the ``pyside6`` package (wheel) is almost empty,
+and only includes references to other packages that are required
+to properly use all the modules.
+This packages are:
+
+* ``pyside6-essentials``, `essential Qt modules <https://pypi.org/project/PySide6-Essentials/>`_,
+* ``pyside6-addons``, `additional Qt modules <https://pypi.org/project/PySide6-Addons/>`_,
+* ``shiboken6``, a utility Python module.
+
+You can verify this by running ``pip list`` to check the installed
+packages in your Python (virtual) environment::
+
+ (env) % pip list
+ Package Version
+ ------------------ -------
+ pip 22.0.4
+ PySide6 6.3.0
+ PySide6-Addons 6.3.0
+ PySide6-Essentials 6.3.0
+ setuptools 58.1.0
+ shiboken6 6.3.0
+
+Both ``pyside6-essentials`` and ``pyside6-addons`` contain Qt binaries
+(``.so``, ``.dll``, or ``.dylib``) that are used by the Python wrappers
+that enable you to use the Qt modules from Python.
+For example, in the ``QtCore`` module, you will find
+on Linux:
+
+* ``PySide6/QtCore.abi3.so``, and
+* ``PySide6/Qt/lib/libQt6Core.so.6``
+
+inside the ``site-packages`` directory of your (virtual) environment.
+The first is the *importable* module which depends on the second file
+which is the original QtCore library.
+
+.. note:: The package ``shiboken6-generator`` is not a dependency,
+ and it's not available on PyPi. The reason, is that it depends on
+ ``libclang``, which is a large library that we don't package, and
+ requires a special configuration for you to use. Check the `Shiboken
+ Documentation`_ for more details.
+
+..
+ Adding the full URL because it's a different sphinx project.
+.. _`Shiboken Documentation`: https://doc.qt.io/qtforpython/shiboken6/gettingstarted.html
+
+Tools Included
+--------------
+
+PySide6 comes bundled with a set of tools that assist in making the development experience with
+PySide6 more efficient. The list of tools can be found :ref:`here <package_tools>`.
#excludes qdoc statements that are not relevant for PySide6
defines += qtforpython
+# Dummies for Apple platform versions
+macro.macOSMinSdkVersion = ""
+macro.macOSMinXcodeVersion = ""
+macro.macOSMinVersion = ""
+macro.macOSMaxVersionTested = ""
+
+macro.iOSMinSdkVersion = ""
+macro.iOSMinXcodeVersion = ""
+macro.iOSMinVersion = ""
+macro.iOSMaxVersionTested = ""
+
+macro.visionOSMinSdkVersion = ""
+macro.visionOSMinXcodeVersion = ""
+macro.visionOSMinVersion = ""
+macro.visionOSMaxVersionTested = ""
+
includepaths += \
-I @QT_INCLUDE_DIR@ \
-I @mkspecInclude@ \
duration = round(time.time() - start_time)
if args.verbose or returncode != 0 or err:
- cmd_str = " ".join(command)
- print(f"> Output of: {cmd_str}")
- if out:
- print(out.decode("utf-8"), file=sys.stdout)
- if err:
+ cmd_str = " ".join(command)
+ print(f"> Output of: {cmd_str}")
+ if out:
+ print(out.decode("utf-8"), file=sys.stdout)
+ if err:
print(err.decode("utf-8"), file=sys.stderr)
- print(f"> Finished: {file} {duration}s (exit code {returncode})")
+ print(f"> Finished: {file} {duration}s (exit code {returncode})")
if returncode != 0:
- raise Exception(f"Failing executing the command {command} ({returncode})")
+ raise Exception(f"Failing executing the command {command} ({returncode})")
def get_qdocconf_files():
args = parser.parse_args()
- core_index = Path(args.doc_data_dir) / "webxml" / "qtcore-index.webxml"
+ core_index = Path(args.doc_data_dir) / "qtcore" / "webxml" / "qtcore-index.webxml"
if core_index.is_file():
print(f"qdoc_spawner: {core_index} already exists, bailing out")
sys.exit(0)
try:
# mode: -prepare -no-link-errors
with Pool(int(parallel)) as p:
- p.starmap(run_qdoc, [(str(f), ["-prepare", "-no-link-errors"], args) for f in files_prepare])
+ files = [(str(f), ["-prepare", "-no-link-errors"], args) for f in files_prepare]
+ p.starmap(run_qdoc, files)
# mode: -single-exec
with Pool(int(parallel)) as p:
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
Tool to run qtattributionsscanner and convert its output to rst
--- /dev/null
+include(@QT_SRC_DIR@/../qtgraphs/src/doc/qtgraphs.qdocconf)
+include(../pyside-config.qdocconf)
--- /dev/null
+include(@QT_SRC_DIR@/../qthttpserver/doc/config/qthttpserver.qdocconf)
+include(../pyside-config.qdocconf)
--- /dev/null
+include(@QT_SRC_DIR@/../qtlocation/src/location/doc/qtlocation.qdocconf)
+include(../pyside-config.qdocconf)
--- /dev/null
+include(@QT_SRC_DIR@/../qtquick3d/src/quick3d/doc/qtquick3d.qdocconf)
+include(../pyside-config.qdocconf)
--- /dev/null
+include(@QT_SRC_DIR@/../qtscxml/src/statemachine/doc/qtstatemachine.qdocconf)
+includepaths += -I @QT_SRC_DIR@/../qtscxml/src/scxml/doc
+include(../pyside-config.qdocconf)
--- /dev/null
+include(@QT_SRC_DIR@/../qtwebview/src/webview/doc/qtwebview.qdocconf)
+include(../pyside-config.qdocconf)
+++ /dev/null
-.. _quick-start:
-
-Quick start
-===========
-
-New to Qt? Check also the :ref:`faq-section` section at the end of this page.
-In case you own a Qt License, please refer to :ref:`commercial-page`.
-
-.. note:: Having Qt installed in your system will not interfere with your
- PySide6 installation if you do it via ``pip install``, because the Python
- packages (wheels) include already Qt binaries. Most notably, style plugins
- from the system won't have any effect on PySide applications.
-
-Requirements
-------------
-
-Before you can install |project|, first you must install the following software:
-
-* Python 3.7+,
-* We **highly** recommend using a virtual environment, such as
- `venv <https://docs.python.org/3/library/venv.html>`_ or
- `virtualenv <https://virtualenv.pypa.io/en/latest>`_
- and avoid installing PySide6 via ``pip`` in your system.
-
-.. note:: For Windows users, please use the interpreter from https://python.org/download
- rather than the one installed from the Microsoft Store.
-
-Installation
-------------
-
-.. note:: For a commercial installation, refer to :ref:`commercial-page`.
-
-* **Creating and activating an environment**
- You can do this by running the following on a terminal:
-
- * Create environment (Your Python executable might be called ``python3``)::
-
- python -m venv env
-
- * Activate the environment (Linux and macOS)::
-
- source env/bin/activate
-
- * Activate the environment (Windows)::
-
- env\Scripts\activate.bat
-
- Check this animation on how to do it:
-
- .. image:: https://qt-wiki-uploads.s3.amazonaws.com/images/8/8a/Pyside6_install.gif
- :alt: Installation GIF
-
-* **Installing PySide6**
-
- Now you are ready to install the |project| packages using ``pip``.
- From the terminal, run the following command:
-
- * For the latest version::
-
- pip install pyside6
-
- * For a specific version, like 6.4.1::
-
- pip install pyside6==6.4.1
-
- * It is also possible to install a specific snapshot from our servers.
- To do so, you can use the following command::
-
- pip install --index-url=https://download.qt.io/snapshots/ci/pyside/6.4/latest pyside6 --trusted-host download.qt.io
-
- .. note:: Starting with 6.4.3, PySide6 can be used from inside a
- `conda <https://conda.io>`_ environment, but any manual changes you make to
- the ``qt.conf`` file will be ignored. If you want to set custom values to
- the Qt configuration, set them in a ``qt6.conf`` file instead.
- Read more about `qt.conf`_.
-
-.. _`qt.conf`: https://doc.qt.io/qt-6/qt-conf.html
-
-* **Test your installation**
-
- Now that you have |project| installed, test your setup by running the following Python
- constructs to print version information::
-
- import PySide6.QtCore
-
- # Prints PySide6 version
- print(PySide6.__version__)
-
- # Prints the Qt version used to compile PySide6
- print(PySide6.QtCore.__version__)
-
-.. note:: For more information about what's included in the ``pyside6``
- package, check :ref:`package_details`.
-
-Create a Simple Qt Widgets Application
---------------------------------------
-
-Your |project| setup is ready. You can explore it further by developing a simple application
-that prints "Hello World" in several languages. The following instructions will
-guide you through the development process:
-
-* **Imports**
-
- Create a new file named :code:`hello_world.py`, and add the following imports to it.::
-
- import sys
- import random
- from PySide6 import QtCore, QtWidgets, QtGui
-
- The |pymodname| Python module provides access to the Qt APIs as its submodule.
- In this case, you are importing the :ref:`QtCore`, :ref:`QtWidgets`, and :ref:`QtGui` submodules.
-
-* **Main Class**
-
- Define a class named :code:`MyWidget`, which extends :ref:`QWidget` and
- includes a :ref:`QPushButton` and :ref:`QLabel`.::
-
- class MyWidget(QtWidgets.QWidget):
- def __init__(self):
- super().__init__()
-
- self.hello = ["Hallo Welt", "Hei maailma", "Hola Mundo", "Привет мир"]
-
- self.button = QtWidgets.QPushButton("Click me!")
- self.text = QtWidgets.QLabel("Hello World",
- alignment=QtCore.Qt.AlignCenter)
-
- self.layout = QtWidgets.QVBoxLayout(self)
- self.layout.addWidget(self.text)
- self.layout.addWidget(self.button)
-
- self.button.clicked.connect(self.magic)
-
- @QtCore.Slot()
- def magic(self):
- self.text.setText(random.choice(self.hello))
-
- The ``MyWidget`` class has the :code:`magic` member function that randomly
- chooses an item from the :code:`hello` list. When you click the button, the
- :code:`magic` function is called.
-
-* **Application execution**
-
- Now, add a main function where you instantiate :code:`MyWidget` and :code:`show` it.::
-
- if __name__ == "__main__":
- app = QtWidgets.QApplication([])
-
- widget = MyWidget()
- widget.resize(800, 600)
- widget.show()
-
- sys.exit(app.exec())
-
- Run your example by writing the following command: :command:`python hello_world.py`.
-
- Try clicking the button at the bottom to see which greeting you get.
-
- .. image:: images/screenshot_hello.png
- :alt: Hello World application
-
-Create a Simple Quick Application
----------------------------------
-
-To do the same using Qt Quick:
-
-* **Imports**
-
- Create a new file named :code:`hello_world_quick.py`, and add the following imports to it.::
-
- import sys
- from PySide6.QtGui import QGuiApplication
- from PySide6.QtQml import QQmlApplicationEngine
-
-* **Declarative UI**
-
- The UI can be described in the QML language (assigned to a Python variable)::
-
- QML = """
- import QtQuick
- import QtQuick.Controls
- import QtQuick.Layouts
-
- Window {
- width: 300
- height: 200
- visible: true
- title: "Hello World"
-
- readonly property list<string> texts: ["Hallo Welt", "Hei maailma",
- "Hola Mundo", "Привет мир"]
-
- function setText() {
- var i = Math.round(Math.random() * 3)
- text.text = texts[i]
- }
-
- ColumnLayout {
- anchors.fill: parent
-
- Text {
- id: text
- text: "Hello World"
- Layout.alignment: Qt.AlignHCenter
- }
- Button {
- text: "Click me"
- Layout.alignment: Qt.AlignHCenter
- onClicked: setText()
- }
- }
- }
- """
-
- .. note:: Keep in mind ideally this content should go into
- a ``qml`` file, but for simplicity, we are using a string variable.
-
-* **Application execution**
-
- Now, add a main function where you instantiate a :ref:`QQmlApplicationEngine` and
- load the QML::
-
- if __name__ == "__main__":
- app = QGuiApplication(sys.argv)
- engine = QQmlApplicationEngine()
- engine.loadData(QML.encode('utf-8'))
- if not engine.rootObjects():
- sys.exit(-1)
- exit_code = app.exec()
- del engine
- sys.exit(exit_code)
-
-
- .. note:: This is a simplified example. Normally, the QML code should be in a separate
- :code:`.qml` file, which can be edited by design tools.
-
-.. _faq-section:
-
-Frequently Asked Questions
---------------------------
-
-Here you can find a couple of common questions and situations that will
-clarify questions before you start programming.
-
-.. grid:: 1 3 3 3
- :gutter: 2
-
- .. grid-item-card:: What is Qt
- :link: faq/whatisqt.html
-
- Qt, QML, Widgets... What is the difference?
-
- .. grid-item-card:: Compatible IDEs
- :link: faq/whichide.html
-
- Which IDEs are compatible with PySide?
-
- .. grid-item-card:: Binding Generation
- :link: faq/whatisshiboken.html
-
- What is Shiboken?
-
- .. grid-item-card:: File types
- :link: faq/typesoffiles.html
-
- File Types in PySide
-
- .. grid-item-card:: App distribution
- :link: faq/distribution.html
-
- Distributing your application to other systems and platforms
-
- .. grid-item-card:: Why Qt for Python?
- :link: faq/whyqtforpython.html
-
- As a Qt/C++ developer, why should I consider Qt for Python?
-
-.. toctree::
- :hidden:
-
- faq/whatisqt.rst
- faq/whichide.rst
- faq/whatisshiboken.rst
- faq/typesoffiles.rst
- faq/distribution.rst
- faq/whyqtforpython.rst
-
:link-type: ref
drag-and-drop tool for designing Widget UIs (generates ``.ui`` files,
- see :ref:`using_ui_files`).
+ see :ref:`tutorial_uifiles`).
.. grid-item-card:: ``pyside6-uic``
:link: pyside6-uic
the capabilities of Qt's ``QML`` runtime utility by
directly invoking QQmlEngine/QQuickView.
+ .. grid-item-card:: ``pyside6-svgtoqml``
+ :link: pyside6-svgtoqml
+ :link-type: ref
+
+ to convert SVG files to QML files.
+
Translations
~~~~~~~~~~~~
:link-type: ref
a graphical user interface for the ``pyside6-balsam`` tool.
+
+.. toctree::
+ :hidden:
+
+ pyside-assistant
+ pyside-designer
+ pyside-genpyi
+ pyside-linguist
+ pyside-lrelease
+ pyside-lupdate
+ pyside-metaobjectdump
+ pyside-project
+ pyside-qml
+ pyside-qmlcachegen
+ pyside-qmllint
+ pyside-qmltyperegistrar
+ pyside-rcc
+ pyside-uic
+ pyside6-balsam
+ pyside6-balsamui
+ pyside6-qmlimportscanner
+ pyside6-qsb
When your application is finished, you will save your design in a ``.ui``
file. This ``.ui`` file can later be converted into a Python file,
with the help of the :ref:`pyside6-uic` tool. You can find
-more details of this process in: :ref:`using_ui_files`.
+more details of this process in: :ref:`tutorial_uifiles`.
If you are interested in more functionality of the tool, you can check
the official `Qt Widgets Designer Manual`_.
simplify the registration process.
If you are interested in all the details of this specific case, you can
-check the :ref:`task-menu-extension-example`.
+check the :ref:`example_designer_taskmenuextension`.
.. _`Qt Widgets Designer`: https://doc.qt.io/qt-6/qtdesigner-manual.html
.. _`Qt Widgets Designer Manual`: https://doc.qt.io/qt-6/qtdesigner-manual.html
For more information on how to use this tool, read Qt's documentation
here: `Qt Linguist`_. Read more about how to translate PySide
-applications here: :ref:`translations`.
+applications here: :ref:`tutorial_translations`.
.. image:: pyside6-linguist_screenshot.webp
:width: 500
``pyside6-lrelease`` is a command line tool wrapping `lrelease`_. It produces
``.qm`` files out of ``.ts`` files. The ``.qm`` file format is a compact binary
format that the localized application uses. It provides extremely fast lookup
-for translations (see :ref:`translations`).
+for translations (see :ref:`tutorial_translations`).
Usage
-----
-To convert a ``.ts`` file of the :ref:`qt-linguist-example`
+To convert a ``.ts`` file of the :ref:`example_widgets_linguist`
into its binary representation, run:
.. code-block:: bash
``pyside6-lupdate`` is a command line tool wrapping `lupdate`_. It finds
translatable strings in Python, ``.ui``, and ``.qml`` files and generates or
-updates ``.ts`` files (see :ref:`translations`).
+updates ``.ts`` files (see :ref:`tutorial_translations`).
Usage
-----
-To create or update the ``.ts`` file of the :ref:`qt-linguist-example`,
+To create or update the ``.ts`` file of the :ref:`example_widgets_linguist`,
run:
.. code-block:: bash
*build*
Builds the project, generating the required build artifacts
- (see :ref:`using_ui_files`, :ref:`using_qrc_files`).
+ (see :ref:`tutorial_uifiles`, :ref:`tutorial_qrcfiles`).
*run*
Builds the project and runs the main.
Deploys the application (see see :ref:`pyside6-deploy`).
*lupdate*
- Updates translation (.ts) files (see :ref:`translations`).
+ Updates translation (.ts) files (see :ref:`tutorial_translations`).
*clean*
Cleans the build artifacts.
pyside6-qml
===========
-``pyside6-qml`` mimics some capabilities of Qt's `qml <qml_runtime>`_ runtime utility by directly
+``pyside6-qml`` mimics some capabilities of Qt's `qml`_ runtime utility by directly
invoking QQmlEngine/QQuickView. It enables prototyping with QML/QtQuick without the need to write
-any Python code that loads the QML files either through `QQmlApplicationEngine <qqmlappengine>`_ or
-the `QQuickView <qquickview>`_ class. The tool also detects the QML classes implemented in Python
+any Python code that loads the QML files either through `QQmlApplicationEngine`_ or
+the `QQuickView`_ class. The tool also detects the QML classes implemented in Python
and registers them with the QML type system.
Usage
-----
-Consider the example `Extending QML - Plugins Example <extending_qml_example>`_. This example does
+Consider the example `Extending QML - Plugins Example`_. This example does
not have a Python file with a ``main`` function that initializes a QmlEngine to load the QML file
``app.qml``. You can run the example by running
* **--verbose/-v**: Run ``pyside6-qml`` in verbose mode. When run in this mode, pyside6-qml prints
log messages during various stages of processing.
-Options that align with `QML <qml_runtime>`_ runtime utility
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Options that align with the `qml`_ runtime utility
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* **--app-typ/-a**: Specifies which application class to use. It takes one of the three values -
``core, gui, widget``. The default value is *gui*.
* **--disable-context-sharing**: Disable the use of a shared GL context for QtQuick Windows".
-.. _`qml_runtime`: https://doc.qt.io/qt-6/qtquick-qml-runtime.html
-.. _`qqmlappengine`: https://doc.qt.io/qt-6/qqmlapplicationengine.html
-.. _`qquickview`: https://doc.qt.io/qt-6/qquickview.html
-.. _`extending_qml_example`: https://doc.qt.io/qtforpython-6/examples/example_qml_tutorials_extending-qml_chapter6-plugins.html
+.. _`qml`: https://doc.qt.io/qt-6/qtquick-qml-runtime.html
+.. _`QQmlApplicationEngine`: https://doc.qt.io/qt-6/qqmlapplicationengine.html
+.. _`QQuickView`: https://doc.qt.io/qt-6/qquickview.html
+.. _`Extending QML - Plugins Example`: https://doc.qt.io/qtforpython-6/examples/example_qml_tutorials_extending-qml_chapter6-plugins.html
For additional options, you can use ``pyside6-rcc -h`` in order to get
more information about additional options.
-Visit the tutorial :ref:`using_qrc_files` for a hands-on example.
+Visit the tutorial :ref:`tutorial_qrcfiles` for a hands-on example.
.. _`rcc`: https://doc.qt.io/qt-6/rcc.html
-
is in charge of positioning all the elements like your design.
To use this Python file, you should follow our tutorial in
-:ref:`using_ui_files`, but in summary, it is mainly importing the class
+:ref:`tutorial_uifiles`, but in summary, it is mainly importing the class
from the generated file and setting it up in your code:
.. code-block:: Python
.. note:: Remember that you need to have a class corresponding to the base
form you selected in :ref:`pyside6-designer`, a ``QWidget``, or ``QDialog``,
or ``QMainWindow``, etc, in order for ``setupUi`` to work. Check
- :ref:`using_ui_files` for more information.
+ :ref:`tutorial_uifiles` for more information.
.. warning:: Do not modify the content of the generated Python file from your
``.ui`` file, otherwise everything will be lost when you re-generate it.
.. _`uic`: https://doc.qt.io/qt-6/uic.html
-
pyside6-balsam
==============
-``pyside6-qsb`` is a tool that wraps the `balsam <Balsam Asset Import Tool>`_
+``pyside6-qsb`` is a tool that wraps the `balsam <Balsam Asset Import Tool_>`_
tool provided with Qt Quick 3D. The Balsam tool is a command line application
that is part of Qt Quick 3D's asset conditioning pipeline. The purpose is to
take assets created in digital content creation tools like `Maya`_, `3ds Max`_
.. _Maya: https://www.autodesk.com/products/maya/overview
.. _3ds Max: https://www.autodesk.com/products/3ds-max/overview
.. _Blender: https://www.blender.org/
-
.. _Maya: https://www.autodesk.com/products/maya/overview
.. _3ds Max: https://www.autodesk.com/products/3ds-max/overview
.. _Blender: https://www.blender.org/
-
Usage
-----
-Invoking the tool in the directory of the :ref:`filesystemexplorer_example`
+Invoking the tool in the directory of the :ref:`example_quickcontrols_filesystemexplorer`
example using:
.. code-block:: bash
pyside6-qsb
===========
-``pyside6-qsb`` is a tool that wraps the `qsb <QSB Manual>`_ tool. qsb is a
+``pyside6-qsb`` is a tool that wraps the `qsb <QSB Manual_>`_ tool. qsb is a
command line tool provided by the `Qt Shader Tools`_ module. It integrates
third-party libraries such as `glslang`_ and `SPIRV-Cross`_, optionally invokes
external tools, such as ``fxc`` or ``spirv-opt``, and generates .qsb files.
--- /dev/null
+.. _pyside6-svgtoqml:
+
+pyside6-svgtoqml
+================
+
+``pyside6-svgtoqml`` is a command line tool that converts SVG files to
+QML files. This QML file can then be used as a component in Qt Quick
+applications.
+
+.. note::
+
+ This tool is a wrapper around the Qt `svgtoqml <SVGtoQML Manual_>`_
+ tool. For more information on how to use this tool, refer to the
+ `svgtoqml <SVGtoQML Manual_>`_ manual.
+
+.. _`SVGtoQML Manual`: https://doc.qt.io/qt-6/qtqml-tooling-svgtoqml.html
+.. _tutorial_clickablebutton:
+
+
Using a Simple Button
=====================
+.. _tutorial_dialog:
+
+
Creating a Dialog Application
=============================
+.. _tutorial_qml:
+
+
Your First QtQuick/QML Application
**********************************
this tutorial, we will show how to make a simple "Hello World"
application with PySide6 and QML.
-A PySide6/QML application consists, at least, of two different files -
+A PySide6/QML application consists, mainly, of two different files -
a file with the QML description of the user interface, and a python file
-that loads the QML file. To make things easier, let's save both files in
-the same directory.
+that loads the QML file.
-Here is a simple QML file called :code:`view.qml`:
+Here is a simple QML file called :code:`Main.qml`:
.. code-block:: javascript
the text appear centered within the object with :code:`id: main`,
which is the Rectangle in this case.
+Put the file into into a directory named :code:`App` along
+with a file named :code:`qmldir` to describe a basic QML module:
+
+.. code-block:: text
+
+ module App
+ Main 254.0 Main.qml
+
Now, let's see how the code looks on the PySide6.
Let's call it :code:`main.py`:
.. code-block:: python
import sys
- from PySide6.QtWidgets import QApplication
+ from PySide6.QtGui import QGuiApplication
from PySide6.QtQuick import QQuickView
if __name__ == "__main__":
- app = QApplication()
+ app = QGuiApplication()
view = QQuickView()
-
- view.setSource("view.qml")
+ view.engine().addImportPath(sys.path[0])
+ view.loadFromModule("App", "Main")
view.show()
- sys.exit(app.exec())
+ ex = app.exec()
+ del view
+ sys.exit(ex)
If you are already familiar with PySide6 and have followed our
tutorials, you have already seen much of this code.
-The only novelties are that you must :code:`import QtQuick` and set the
-source of the :code:`QQuickView` object to the URL of your QML file.
+The only novelties are that you must :code:`import QtQuick`,
+add the directory to the import paths, and instruct the
+:code:`QQuickView` to load our module.
Then, similar to what you do with any Qt widget, you call
:code:`QQuickView.show()`.
-.. _using_qrc_files:
+.. _tutorial_qrcfiles:
+
Using ``.qrc`` Files (``pyside6-rcc``)
**************************************
Changes in the code
===================
-As you are modifying an existing example, you need to modify the following
-lines:
+As you are modifying an existing example, you need to modify the
+``player.py`` file. At the top, change
.. code-block:: python
from PySide6.QtGui import QIcon, QKeySequence
- playIcon = self.style().standardIcon(QStyle.SP_MediaPlay)
- previousIcon = self.style().standardIcon(QStyle.SP_MediaSkipBackward)
- pauseIcon = self.style().standardIcon(QStyle.SP_MediaPause)
- nextIcon = self.style().standardIcon(QStyle.SP_MediaSkipForward)
- stopIcon = self.style().standardIcon(QStyle.SP_MediaStop)
-and replace them with the following:
+to:
.. code-block:: python
from PySide6.QtGui import QIcon, QKeySequence, QPixmap
- playIcon = QIcon(QPixmap(":/icons/play.png"))
- previousIcon = QIcon(QPixmap(":/icons/previous.png"))
- pauseIcon = QIcon(QPixmap(":/icons/pause.png"))
- nextIcon = QIcon(QPixmap(":/icons/forward.png"))
- stopIcon = QIcon(QPixmap(":/icons/stop.png"))
-This ensures that the new icons are used instead of the default ones provided
-by the application theme.
-Notice that the lines are not consecutive, but are in different parts
-of the file.
-
-After all your imports, add the following
+Below the imports, add the following:
.. code-block:: python
import rc_icons
-Now, the constructor of your class should look like this:
+In the ``MainWindow.__init__()`` function, replace the code
+loading the icons from the theme by your custom icons:
.. code-block:: python
- def __init__(self):
- super(MainWindow, self).__init__()
-
- self.playlist = QMediaPlaylist()
- self.player = QMediaPlayer()
+ playIcon = QIcon(QPixmap(":/icons/play.png"))
+ self._play_action = tool_bar.addAction(playIcon, "Play")
- toolBar = QToolBar()
- self.addToolBar(toolBar)
+When doing this for all icons, the constructor of your class should
+look like this:
- fileMenu = self.menuBar().addMenu("&File")
- openAction = QAction(QIcon.fromTheme("document-open"),
- "&Open...", self, shortcut=QKeySequence.Open,
- triggered=self.open)
- fileMenu.addAction(openAction)
- exitAction = QAction(QIcon.fromTheme("application-exit"), "E&xit",
- self, shortcut="Ctrl+Q", triggered=self.close)
- fileMenu.addAction(exitAction)
+.. code-block:: python
- playMenu = self.menuBar().addMenu("&Play")
+ def __init__(self):
+ super().__init__()
+
+ self._playlist = []
+ self._playlist_index = -1
+ self._audio_output = QAudioOutput()
+ self._player = QMediaPlayer()
+ self._player.setAudioOutput(self._audio_output)
+
+ self._player.errorOccurred.connect(self._player_error)
+
+ tool_bar = QToolBar()
+ self.addToolBar(tool_bar)
+
+ file_menu = self.menuBar().addMenu("&File")
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.DocumentOpen)
+ open_action = QAction(icon, "&Open...", self,
+ shortcut=QKeySequence.Open, triggered=self.open)
+ file_menu.addAction(open_action)
+ tool_bar.addAction(open_action)
+ icon = QIcon.fromTheme(QIcon.ThemeIcon.ApplicationExit)
+ exit_action = QAction(icon, "E&xit", self,
+ shortcut="Ctrl+Q", triggered=self.close)
+ file_menu.addAction(exit_action)
+
+ play_menu = self.menuBar().addMenu("&Play")
playIcon = QIcon(QPixmap(":/icons/play.png"))
- self.playAction = toolBar.addAction(playIcon, "Play")
- self.playAction.triggered.connect(self.player.play)
- playMenu.addAction(self.playAction)
+ self._play_action = tool_bar.addAction(playIcon, "Play")
+ self._play_action.triggered.connect(self._player.play)
+ play_menu.addAction(self._play_action)
previousIcon = QIcon(QPixmap(":/icons/previous.png"))
- self.previousAction = toolBar.addAction(previousIcon, "Previous")
- self.previousAction.triggered.connect(self.previousClicked)
- playMenu.addAction(self.previousAction)
+ self._previous_action = tool_bar.addAction(previousIcon, "Previous")
+ self._previous_action.triggered.connect(self.previous_clicked)
+ play_menu.addAction(self._previous_action)
pauseIcon = QIcon(QPixmap(":/icons/pause.png"))
- self.pauseAction = toolBar.addAction(pauseIcon, "Pause")
- self.pauseAction.triggered.connect(self.player.pause)
- playMenu.addAction(self.pauseAction)
+ self._pause_action = tool_bar.addAction(pauseIcon, "Pause")
+ self._pause_action.triggered.connect(self._player.pause)
+ play_menu.addAction(self._pause_action)
nextIcon = QIcon(QPixmap(":/icons/forward.png"))
- self.nextAction = toolBar.addAction(nextIcon, "Next")
- self.nextAction.triggered.connect(self.playlist.next)
- playMenu.addAction(self.nextAction)
+ self._next_action = tool_bar.addAction(nextIcon, "Next")
+ self._next_action.triggered.connect(self.next_clicked)
+ play_menu.addAction(self._next_action)
stopIcon = QIcon(QPixmap(":/icons/stop.png"))
- self.stopAction = toolBar.addAction(stopIcon, "Stop")
- self.stopAction.triggered.connect(self.player.stop)
- playMenu.addAction(self.stopAction)
+ self._stop_action = tool_bar.addAction(stopIcon, "Stop")
+ self._stop_action.triggered.connect(self._ensure_stopped)
+ play_menu.addAction(self._stop_action)
# many lines were omitted
-.. _signals-and-slots:
+.. _tutorial_signals_and_slots:
+
Signals and Slots
=================
-Due to the nature of Qt, ``QObject``\s require a way to communicate, and that's
-the reason for this mechanism to be a **central feature of Qt**.
+Due to the nature of Qt, :class:`~PySide6.QtCore.QObject`\s
+require a way to communicate, and that's the reason for this mechanism to
+be a **central feature of Qt**.
In simple terms, you can understand **Signal and Slots** in the same way you
interact with the lights in your house. When you move the light switch
there are essential differences that make it an unintuitive approach,
like ensuring the type correctness of callback arguments, and some others.
-All classes that inherit from ``QObject`` or one of its subclasses, like
-``QWidget`` can contain signals and slots. **Signals are emitted by objects**
+All classes that inherit from :class:`~PySide6.QtCore.QObject` or one of its
+subclasses, like :class:`~PySide6.QtWidgets.QWidget`, can contain signals and
+slots. **Signals are emitted by objects**
when they change their state in a way that may be interesting to other objects.
This is all the object does to communicate. It does not know or care whether
anything is receiving the signals it emits. This is true information
immediately whenever the first is emitted.)
Qt's widgets have many predefined signals and slots. For example,
-``QAbstractButton`` (base class of buttons in Qt) has a ``clicked()``
-signal and ``QLineEdit`` (single line input field) has a slot named
-``clear()``. So, a text input field with a button to clear the text
-could be implemented by placing a ``QToolButton`` to the right of the
-``QLineEdit`` and connecting its ``clicked()`` signal to the slot
-``clear()``. This is done using the ``connect()`` method of the signal:
+:class:`~PySide6.QtWidgets.QAbstractButton` (base class of buttons in Qt)
+has a ``clicked()`` signal and :class:`~PySide6.QtWidgets.QLineEdit`
+(single line input field) has a slot named ``clear()``.
+So, a text input field with a button to clear the text
+could be implemented by placing a :class:`~PySide6.QtWidgets.QToolButton`
+to the right of the ``QLineEdit`` and connecting its ``clicked()`` signal to the slot
+``clear()``. This is done using the :meth:`~PySide6.QtCore.Signal.connect`
+method of the signal:
.. code-block:: python
line_edit = QLineEdit()
button.clicked.connect(line_edit.clear)
-``connect()`` returns a ``QMetaObject.Connection`` object, which can be
-used with the ``disconnect()`` method to sever the connection.
+:meth:`~PySide6.QtCore.Signal.connect` returns a
+:class:`~PySide6.QtCore.QMetaObject.Connection` object, which can be
+used with the :meth:`~PySide6.QtCore.Signal.disconnect` method to sever
+the connection.
Signals can also be connected to free functions:
`Signal-Slot Editor <https://doc.qt.io/qt-6/designer-connection-mode.html>`_
of *Qt Widgets Designer*.
+The :meth:`~PySide6.QtCore.Signal.connect` function takes an optional parameter
+of :class:`~PySide6.QtCore.Qt.ConnectionType` that specifies the behavior
+with regards to threads and event loops.
+
The Signal Class
----------------
When writing classes in Python, signals are declared as class level
-variables of the class ``QtCore.Signal()``. A QWidget-based button
-that emits a ``clicked()`` signal could look as
-follows:
+variables of the class :class:`~PySide6.QtCore.Signal`.
+A :class:`~PySide6.QtWidgets.QWidget`-based button that emits a
+``clicked()`` signal could look as follows:
.. code-block:: python
--------------
Slots in QObject-derived classes should be indicated by the decorator
-``@QtCore.Slot()``. Again, to define a signature just pass the types
-similar to the ``QtCore.Signal()`` class.
+:deco:`~PySide6.QtCore.Slot`. Again, to define a signature just pass the types
+similar to the :class:`~PySide6.QtCore.Signal` class.
.. code-block:: python
function that is being decorated.
We recommend marking all methods used by signal connections with a
-``@QtCore.Slot()`` decorator. Not doing causes run-time overhead due to the
-method being added to the ``QMetaObject`` when creating the connection. This is
-particularly important for ``QObject`` classes registered with QML, where
+:deco:`~PySide6.QtCore.Slot` decorator. Not doing causes run-time overhead
+due to the method being added to the ``QMetaObject`` when creating the connection.
+This is particularly important for ``QObject`` classes registered with QML, where
missing decorators can introduce bugs.
Missing decorators can be diagnosed by setting activating warnings of the
for m in range(mo.methodOffset(), mo.methodCount()):
print(mo.method(m).methodSignature())
-Slots should be decorated using :ref:`@Slot <slot-decorator>`.
+Slots should be decorated using :deco:`~PySide6.QtCore.Slot`.
+.. _tutorial_tablewidget:
+
+
Displaying Data Using a Table Widget
====================================
-.. _translations:
+.. _tutorial_translations:
+
Translating Applications
========================
`Qt Linguist`_ and
its related tools can be used to provide translations for applications.
-The :ref:`qt-linguist-example` example illustrates this. The example is
+The :ref:`example_widgets_linguist` example illustrates this. The example is
very simple, it has a menu and shows a list of programming languages with
multiselection.
To avoid having to ship the ``.qm`` files, it is recommend
to put them into a Qt resource file along with icons and other
-applications resources (see :ref:`using_qrc_files`).
+applications resources (see :ref:`tutorial_qrcfiles`).
The resource file ``linguist.qrc`` provides the ``example_de.qm``
under ``:/translations``:
The `GNU gettext`_ module
can be used to provide translations for applications.
-The :ref:`gettext-example` example illustrates this. The example is
+The :ref:`example_widgets_gettext` example illustrates this. The example is
very simple, it has a menu and shows a list of programming languages with
multiselection.
+.. _tutorial_treewidget:
+
+
Displaying Data Using a Tree Widget
===================================
-.. _using_ui_files:
+.. _tutorial_uifiles:
+
Using ``.ui`` files from Designer or QtCreator with ``QUiLoader`` and ``pyside6-uic``
*************************************************************************************
The widget needs to be provided as a Python module, as shown by
the :ref:`widgetbinding-example` (file ``wigglywidget.py``) or
-the :ref:`task-menu-extension-example` (file ``tictactoe.py``).
+the :ref:`example_designer_taskmenuextension` (file ``tictactoe.py``).
Registering this with *Qt Widgets Designer* is done by providing
a registration script named ``register*.py`` and pointing
+.. _tutorial_widgets:
+
+
Your First QtWidgets Application
*********************************
Finally, we call `app.exec()` to enter the Qt main loop and start
to execute the Qt code. In reality, it is only here where the label
is shown, but this can be ignored for now.
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
-.. _widgetstyling:
+.. _tutorial_widgetstyling:
+
Styling the Widgets Application
===============================
+.. _tutorial_add_chart:
+
+
Chapter 5 - Add a chart view
=============================
:linenos:
:lines: 3-
:emphasize-lines: 2-3,6,22-36,47-49
-
-
+.. _tutorial_add_mainwindow:
+
+
Chapter 3 - Create an empty QMainWindow
==========================================
+.. _tutorial_add_tableview:
+
+
Chapter 4 - Add a QTableView
=============================
:linenos:
:lines: 3-
:emphasize-lines: 46-47
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import argparse
import pandas as pd
args = options.parse_args()
data = read_data(args.file)
print(data)
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import argparse
import pandas as pd
args = options.parse_args()
data = read_data(args.file)
print(data)
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
import argparse
window.show()
sys.exit(app.exec())
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Slot
from PySide6.QtGui import QAction, QKeySequence
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
import argparse
window.show()
sys.exit(app.exec())
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWidgets import (QHBoxLayout, QHeaderView, QSizePolicy,
QTableView, QWidget)
# QTableView Headers
self.horizontal_header = self.table_view.horizontalHeader()
self.vertical_header = self.table_view.verticalHeader()
- self.horizontal_header.setSectionResizeMode(
- QHeaderView.ResizeToContents
- )
- self.vertical_header.setSectionResizeMode(
- QHeaderView.ResizeToContents
- )
+ self.horizontal_header.setSectionResizeMode(QHeaderView.ResizeToContents)
+ self.vertical_header.setSectionResizeMode(QHeaderView.ResizeToContents)
self.horizontal_header.setStretchLastSection(True)
# QWidget Layout
self.main_layout = QHBoxLayout()
size = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
- ## Left layout
+ # Left layout
size.setHorizontalStretch(1)
self.table_view.setSizePolicy(size)
self.main_layout.addWidget(self.table_view)
# Set the layout to the QWidget
self.setLayout(self.main_layout)
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
-from PySide6.QtCore import Slot
from PySide6.QtGui import QAction, QKeySequence
from PySide6.QtWidgets import QMainWindow
self.menu = self.menuBar()
self.file_menu = self.menu.addMenu("File")
- ## Exit QAction
+ # Exit QAction
exit_action = QAction("Exit", self)
exit_action.setShortcut(QKeySequence.Quit)
exit_action.triggered.connect(self.close)
# Window dimensions
geometry = self.screen().availableGeometry()
self.setFixedSize(geometry.width() * 0.8, geometry.height() * 0.7)
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Qt, QAbstractTableModel, QModelIndex
from PySide6.QtGui import QColor
return Qt.AlignRight
return None
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
import argparse
window.show()
sys.exit(app.exec())
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QDateTime, Qt
from PySide6.QtGui import QPainter
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
-from PySide6.QtCore import Slot
from PySide6.QtGui import QAction, QKeySequence
from PySide6.QtWidgets import QMainWindow
# Window dimensions
geometry = self.screen().availableGeometry()
self.setFixedSize(geometry.width() * 0.8, geometry.height() * 0.7)
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Qt, QAbstractTableModel, QModelIndex
from PySide6.QtGui import QColor
return Qt.AlignRight
return None
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
import argparse
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import QDateTime, Qt
from PySide6.QtGui import QPainter
# Getting the color from the QChart to use it on the QTableView
color_name = self.series.pen().color().name()
self.model.color = f"{color_name}"
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Slot
from PySide6.QtGui import QAction, QKeySequence, QScreen
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import Qt, QAbstractTableModel, QModelIndex
from PySide6.QtGui import QColor
+.. _tutorial_filter_data:
+
+
Chapter 2 - Filtering data
===========================
+.. _datavisualize_index:
+
+
Data Visualization Tool Tutorial
*********************************
+.. _tutorial_plot_datapoints:
+
+
Chapter 6 - Plot the data in the ChartView
===========================================
+.. _tutorial_read_data:
+
+
Chapter 1 - Reading data from a CSV
===================================
+.. _tutorial_mixed_debugging:
+
+
How To Debug a C++ Extension of a PySide6 Application?
******************************************************
Build PySide6 using the Qt 6 that you built earlier
----------------------------------------------------
-Follow the steps mentioned `Getting Started - Qt for Python
-<https://doc.qt.io/qtforpython/gettingstarted/index.html>`_
+Follow the steps mentioned in :doc:`Building from Source <../../building_from_source/index>` guide.
You may manually select the modules to install using the ``--module-subset`` cli
argument for `setup.py`. This was my installation script
+.. _tutorial_qml_debugging:
+
+
Using Qt Creator's QML Debugger for a PySide6 QML Application
*************************************************************
.. image:: breakpoint_cpp.png
:alt: breakpoint cpp
:align: center
-
--- /dev/null
+(tutorial_boottoqt_raspberrypi)=
+
+# Deploying a PySide6 Application to Boot to Qt on Raspberry Pi
+
+This tutorial provides a step-by-step guide to setting up Boot to Qt on a Raspberry Pi and deploying
+a PySide6 application. You will learn how to download and flash the Boot to Qt image, create a
+sample PySide6 application, and manually deploy it to the Raspberry Pi. Additionally, the tutorial
+covers automating the deployment process with a shell script and offers tips for improving the
+manual deployment steps.
+
+> **Note**: This tutorial was tested on Unix hosts. Windows hosts might not work as expected.
+
+> **Note**: Boot to Qt comes with PySide6 pre-installed in its Python environment, so you can start
+> developing and deploying PySide6 applications right away without needing to install additional
+> packages. Each first minor release of Qt for Device Creation (eg: 6.8.0) does not include PySide6.
+> The further sub-releases (eg: 6.8.1, 6.8.2) includes PySide6.
+
+## Prerequisites
+
+- Raspberry Pi 4/5 (aarch64)
+- SD card to flash the Boot to Qt image
+- `ssh` and `scp` tools available
+- Network connectivity between your computer and the Raspberry Pi
+- Read the [Boot to Qt Quick Start for Raspberry Pi][boot2qt-quick-start] guide
+
+## Setting Up Boot to Qt on Raspberry Pi
+
+1. **Download Boot to Qt Image**
+ - Obtain the Boot to Qt image for Raspberry Pi as mentioned using the Qt maintenance tool in
+ [Installing Boot to Qt Software Stack][installing-boot2qt]
+ - Ensure the image you downloaded has PySide6 pre-installed in the Python environment.
+
+2. **Flash the Image to an SD Card**
+ - Use Qt Creator to flash the Boot to Qt image to an SD card by following the instructions in
+ [Flashing the Image to an SD Card][flashing-image]
+ - Alternatively, use a tool like `balenaEtcher` or `dd` to write the image download in step 1 to
+ the SD card.
+ - **Using balenaEtcher:**
+ 1. Download and install balenaEtcher from [balena.io][balenaetcher].
+ 2. Open balenaEtcher.
+ 3. Select the Boot to Qt image file.
+ 4. Select the target SD card.
+ 5. Click "Flash!" to start the process.
+ - **Using `dd`:**
+ 1. Identify the SD card device name using `lsblk` or `fdisk -l`.
+ 2. Unmount the SD card partitions.
+ 3. Write the image to the SD card using `dd`:
+
+ ```sh
+ sudo dd if=<path-to-image-file> of=/dev/sdX bs=4M status=progress
+ ```
+ > **Warning:** Ensure you replace `/dev/sdX` with the correct device name of the SD card.
+
+3. **Boot the Raspberry Pi**
+ - Insert the newly flashed SD card into the Raspberry Pi.
+ - Power on your device (make sure you are connected to the network)
+
+4. **Verify Boot to Qt is Running**
+ - Ensure the Raspberry Pi boots into Boot to Qt. The device starts with Boot to Qt Demo Launcher
+ application by default. The IP address is shown if the device is connected to an Ethernet or
+ Wi-Fi network, or to the host computer via USB OTG.
+ - You can check network connectivity by pinging the device.
+
+## Preparing the PySide6 Application
+
+1. **Create a Sample PySide6 Application**
+ - Write a simple PySide6 application:
+
+ ```python
+ # sample_app.py
+ import sys
+ from PySide6.QtWidgets import QApplication, QLabel
+
+ app = QApplication(sys.argv)
+ label = QLabel("Hello from PySide6 on Boot to Qt!")
+ label.show()
+ sys.exit(app.exec())
+ ```
+
+2. **Test Locally**
+ - Run the application on your development machine to ensure it works.
+
+ ```sh
+ python sample_app.py
+ ```
+ > **Note:** Make sure you have PySide6 installed on your local environment. If not, you can install
+ > it using `pip`: `pip install PySide6`.
+
+
+## Deploying the Application to Boot to Qt
+
+1. **Find the Raspberry Pi's IP Address**
+ - For finding the IP Address of the device, refer to the section `Verify Boot to Qt is Running`.
+
+2. **Transfer the Application Using `scp`**
+ - Copy the application to the Raspberry Pi:
+
+ ```sh
+ scp sample_app.py username@<RASPBERRY_PI_IP>:
+ ```
+ > **Note:** This command copies the file to the home directory of the user `username`. You can
+ > specify a different directory if needed.
+
+3. **SSH into the Raspberry Pi**
+ - Connect to the Raspberry Pi via SSH:
+
+ ```sh
+ ssh username@<RASPBERRY_PI_IP>
+ ```
+
+4. **Run the Application on the Raspberry Pi**
+ - Execute the application:
+
+ ```sh
+ python3 /home/username/sample_app.py
+ ```
+
+## Automating Deployment with a Shell Script
+
+You can streamline the deployment process with a shell script.
+
+```sh
+#!/bin/bash
+
+# Variables
+APP_NAME="sample_app.py"
+PI_USER="username"
+PI_IP="<RASPBERRY_PI_IP>"
+REMOTE_PATH="/home/username/"
+
+# Copy the application to the Raspberry Pi
+scp $APP_NAME $PI_USER@$PI_IP:$REMOTE_PATH
+
+# Run the application on the Raspberry Pi
+ssh $PI_USER@$PI_IP "python3 $REMOTE_PATH$APP_NAME"
+```
+
+- **Make the Script Executable**
+
+ ```sh
+ chmod +x deploy.sh
+ ```
+
+- **Run the Script**
+
+ ```sh
+ ./deploy.sh
+ ```
+
+## Improving the Manual Deployment Steps
+
+Although SSH and SCP are effective, you can enhance the deployment process:
+
+- **Use SSH Key Authentication**
+ - Set up SSH keys to avoid entering passwords each time and use `ssh-copy-id` tool to copy the
+ public key to the Raspberry Pi.
+
+- **Use `rsync` for Efficient File Transfer**
+ - Alternative to `scp` for transferring files.
+ - `rsync` only transfers changed files.
+
+ ```sh
+ rsync -avz sample_app.py username@<RASPBERRY_PI_IP>:
+ ```
+ > **Note:** This command copies the file to the home directory of the user `username`.
+
+## Conclusion
+
+In this tutorial, we have provided a comprehensive guide to setting up Boot2Qt on a Raspberry Pi and
+deploying a PySide6 application.
+
+[boot2qt-quick-start]: https://doc.qt.io/Boot2Qt/b2qt-qsg-raspberry.html
+[installing-boot2qt]: https://doc.qt.io/Boot2Qt/b2qt-qsg-raspberry.html#installing-boot-to-qt-software-stack-using-qt-maintenance-tool
+[flashing-image]: https://doc.qt.io/Boot2Qt/b2qt-qsg-raspberry.html#installing-boot-to-qt-on-the-target-device
+[qt-creator]: https://doc.qt.io/qtcreator
+[balenaetcher]: https://etcher.balena.io/
+.. _tutorial_expenses:
+
+
Expenses Tool Tutorial
======================
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtCore import Qt, Slot
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtCore import QMargins, Qt, Slot, QSize
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtWidgets import QApplication, QMainWindow
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtWidgets import QApplication, QMainWindow
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtWidgets import QApplication, QMainWindow
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QWidget
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtWidgets import (QApplication, QHeaderView, QHBoxLayout,
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtWidgets import (QApplication, QFormLayout, QHeaderView,
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtCore import Slot
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtCore import Slot
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtCore import Slot
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtCore import Qt, Slot
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtWidgets import QFileSystemModel
from PySide6.QtQuick import QQuickTextDocument
+(tutorial_extendedexplorer)=
+
# Extending the file system explorer example
This tutorial shows how to extend the
-[Filesystem Explorer Example](filesystemexplorer_example)
+[Filesystem Explorer Example](example_quickcontrols_filesystemexplorer)
by adding a simple scheme manager. This feature will allow you to switch color
schemes during the application's runtime. The color schemes will be declared in
JSON format and made available through a custom Python-QML plugin.
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""
This example shows how to customize Qt Quick Controls by implementing a simple filesystem explorer.
# import FileSystemModule.rc_icons
# import FileSystemModule.rc_app
-from scheme_manager import SchemeManager
-from editormodels import FileSystemModel
+from scheme_manager import SchemeManager # noqa: F401
+from editormodels import FileSystemModel # noqa: F401
import PySide6
from PySide6.QtGui import QGuiApplication, QIcon
from PySide6.QtQml import QQmlApplicationEngine
fsm = engine.singletonInstance("FileSystemModule", "FileSystemModel")
fsm.setInitialDirectory(args[0])
- sys.exit(app.exec())
+ exit_code = app.exec()
+ del engine
+ sys.exit(exit_code)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import json
from pathlib import Path
--- /dev/null
+(tutorial_financemanager)=
+
+# Finance Manager Tutorial
+
+## Summary
+
+This tutorial series is structured into three comprehensive parts, each building upon the previous
+one to create a fully functional finance manager application. It demonstrates how PySide6 can be
+used to interact with other popular packages in the Python ecosystem. The series starts with the
+basics of PySide6 and QtQuick, then integrates SQLite for database management using
+[SQLAlchemy], and finally incorporates [FastAPI] and [Pydantic] for server-side operations and
+REST API integration.
+
+### Part 1: Building a Complete PySide6 QtQuick Application
+- **Introduction**: Overview of the tutorial series and prerequisites.
+- **Project Setup**: Setting up the development environment and installing necessary packages.
+- **Creating the Main Window**: Setting up the main window using PySide6 and
+ creating the main QML file.
+- **Adding Basic UI Components**: Adding buttons, labels, input fields, listview, Pie chart and
+ handling user interactions with PySide6 and QML.
+- For more details, see {ref}`tutorial_financemanager_partone`.
+
+### Part 2: Integrating SQLite Database with SQLAlchemy
+- **Setting Up SQLAlchemy**: Installing SQLAlchemy and configuring the SQLite database connection.
+- **Creating Database Models**: Defining database models for finance data.
+- **CRUD Operations**: Implementing part of the CRUD operations through Create, and Read operations
+ and connecting them to the PySide6 application.
+- For more details, see {ref}`tutorial_financemanager_parttwo`.
+
+### Part 3: Using FastAPI and Pydantic for Server-Side Operations
+- **Setting Up FastAPI**: Installing FastAPI and Uvicorn, and creating a basic FastAPI application.
+- **Creating REST Endpoints**: Defining RESTful endpoints for finance data and implementing CRUD
+ operations through the API.
+- **Connecting FastAPI with PySide6**: Making HTTP requests from the PySide6 application and
+ displaying data fetched from the API in the UI.
+- For more details, see {ref}`tutorial_financemanager_partthree`.
+
+Each part ends with instructions on how to deploy the application using {ref}`pyside6-deploy`.
+This structured approach ensures that readers can follow along and understand each concept before
+moving on to the next one, resulting in a robust and scalable finance manager application.
+
+[SQLalchemy]: https://www.sqlalchemy.org/
+[FastApi]: https://fastapi.tiangolo.com/
+[PyDantic]: https://pydantic-docs.helpmanual.io/
+
+```{toctree}
+:maxdepth: 1
+:hidden:
+
+part1/part1.md
+part2/part2.md
+part3/part3.md
--- /dev/null
+(tutorial_financemanager_partone)=
+
+# Finance Manager Tutorial - Part 1
+
+In this tutorial, we will create a finance manager app using QtQuick and PySide6. The app will allow
+you to add your expenses and visualize them using a pie charts based on the category of spending.
+The application is designed in a way that it is compatible with Desktop and Android platforms.
+
+
+
+
+
+To download the complete source code for this tutorial, visit
+{ref}`example_tutorials_finance_manager_part1`.
+
+## Prerequisites
+
+Before we begin, firstly make sure you have Python 3.9+ and PySide6 installed within you Python
+environment. You can install it using pip:
+
+```bash
+pip install PySide6
+```
+
+## Project Design
+
+The finance manager app is a simple app demonstrating the use of PySide6 to integrate QtQuick with
+Python, allowing for a seamless combination of QML for the user interface and Python for the backend
+logic. It will have the following components:
+
+1. Expense List: This list will display all the entered expenses, showing the expense name, amount,
+ category and date. The expenses are organized by their month and year.
+
+2. PieChart: This chart will visualize the expenses based on their categories, giving users a clear
+ overview of their spending habits.
+
+3. Add Expense: A dialog enabling the user to add new expenses.
+
+The overall project structure will be as follows:
+
+```
+finance_manager/
+├── main.py
+├── financemodel.py
+├── Finance/
+│ ├── Main.qml
+│ ├── FinanceView.qml
+│ ├── FinanceDelegate.qml
+│ ├── FinancePieChart.qml
+│ ├── AddDialog.qml
+│ └── qmldir
+```
+## Let's get started!
+
+### Component Overview
+
+In the first part of this tutorial, we will start by creating the Expense List using some
+pre-defined expenses. For this, we will create a new Python file `financemodel.py` that
+defines a class **FinanceModel** that will be used to manage the expenses from Python and expose
+it to QML.
+
+<details>
+<summary class="prominent-summary">financemodel.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part1/financemodel.py
+---
+language: python
+caption: FinanceModel class definition
+linenos: true
+---
+```
+</details>
+
+Here's a brief overview of the `FinanceModel` class, its components and methods:
+
+1. **QML type registration**
+ - The `FinanceModel` class is registered as a QML type using the {deco}`~PySide6.QtQml.QmlElement`
+ decorator. This decorator is used to define a Python class as a QML type, allowing it to be used
+ in QML files.
+ - The `QML_IMPORT_NAME` variable is used to define the name of the module that will be imported
+ in QML to access the `FinanceModel` class.
+2. **Members**
+ - **FinanceRole Enum**: Defines custom roles for the model data, such as `ItemNameRole`,
+ `CategoryRole`, `CostRole`, `DateRole` and `MonthRole`.
+ - **Finance Dataclass**: Represents an individual expense with the attributes `item_name`,
+ `category`, `cost`, `date` and `month`.
+ - **init Method**: Initializes the model with some pre-defined expenses.
+ - **rowCount Method**: Returns the number of items in the model.
+ - **data Method**: Returns the data for a given role and index in the model.
+ - **getCategoryData Method**: Returns a dictionary the total cost for each category in the
+ model. This method has the `@Slot` decorator to make it accessible from QML.
+ - **roleNames Method**: Maps role names to their `QByteArray` values.
+ - **get Method**: A `@Slot` method to get the expense data at a given index.
+ - **append Method**: A `@Slot` method to append a new expense to the model.
+
+For using as a data model in the `ListView` component in QML, the methods `rowCount`, `data`, and
+`roleNames` are required.
+
+Now that we have defined the `FinanceModel` class, let's create the QML components to display the
+expenses. First, we create `Finance/Main.qml` file that will be the main QML file for our app.
+
+<details>
+<summary class="prominent-summary">Main.qml</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part1/Finance/Main.qml
+---
+language: QML
+caption: Main.qml
+linenos: true
+---
+```
+</details>
+
+In the `Main.qml` we import the created `Finance` QML module file and the file has the following components:
+
+1. **ApplicationWindow**:
+ - The main window of the application.
+ - Sets the theme to `Material.Dark` and accent color to `Material.Gray`.
+ - Adjusts the window size to the screen dimensions.
+ - Contains the title "Finance Manager".
+
+2. **ToolBar**:
+ - A toolbar that is only visible on mobile platforms (Android and iOS). Note that PySide6
+ supports only Android, but you can use the same code with Qt C++ for iOS.
+ - Contains a `Label` with the text "Finance Manager".
+
+3. **ColumnLayout**:
+ - A layout that arranges its children in a column.
+ - Fills the entire window.
+
+4. **TabBar**:
+ - Contains two `TabButton` components for switching between **Expense** and **Charts** views.
+
+5. **StackLayout**:
+ - A layout that stacks its children on top of each other.
+ - Contains two `Item`components for the "Expenses" and "Charts" views.
+
+6. **FinanceView**:
+ - A custom component to display the list of expenses.
+ - Binds to the `finance_model`.
+ - This component is defined in the `FinanceView.qml` file.
+
+7. **FinancePieChart**:
+ - A custom component to display a pie chart of expenses by category.
+ - Updates the chart with data from `finance_model.getCategoryData()` when the component is
+ completed.
+ - This component is defined in the `FinancePieChart.qml` file.
+
+8. **FinanceModel**:
+ - The data model created from Python to store finance data. This is imported by import the
+ QML module `Finance` in the `Main.qml` file.
+
+9. **AddDialog**:
+ - A dialog for adding new expense entries.
+ - Appends new entries to `finance_model` and updates the pie chart.
+
+10. **RoundButton**:
+ - A circular button to open the `AddDialog.qml`.
+ - Positioned at the bottom-right corner of the window.
+ - Contains a "+" symbol and has a highlighted appearance.
+
+Now that we have the basic structure of the main QML file, let's create the `FinanceView.qml` file:
+
+<details>
+<summary class="prominent-summary">FinanceView.qml</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part1/Finance/FinanceView.qml
+---
+language: QML
+caption: FinanceView.qml
+linenos: true
+---
+```
+</details>
+
+`FinanceView.qml` contains the following components:
+
+1. **ListView**:
+ - The main container for displaying a list of items.
+ - Fills the entire parent container using `anchors.fill: parent`.
+ - Uses the `financeModel` property as its data model.
+
+2. **property var financeModel**:
+ - A property to hold the data model for the list.
+ - This model is expected to be passed from the parent component. In this case, it is passed from
+ the `Main.qml` file.
+
+3. **delegate**:
+ - Defines how each item in the `ListView` should be displayed.
+ - Uses a custom component `FinanceDelegate` to render each item. This component is defined in the
+ `FinanceDelegate.qml` file.
+ - Sets the width of each delegate to match the width of the `ListView`.
+
+4. **model**:
+ - Binds the `ListView` to the `financeModel` property.
+ - The `ListView` will display items based on the data in `financeModel`.
+
+5. **section**:
+ - The section property is used to group the items in the list view based on the month of the
+ expense.
+
+6. **ScrollBar.vertical**:
+ - Adds a vertical scrollbar to the `ListView`.
+ - Ensures that users can scroll through the list if the content exceeds the visible area.
+
+These components together create a scrollable list view for displaying financial data, with each
+item rendered using the `FinanceDelegate` component.
+
+Next, let's create the `FinanceDelegate.qml` file:
+
+<details>
+<summary class="prominent-summary">FinanceDelegate.qml</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part1/Finance/FinanceDelegate.qml
+---
+language: QML
+caption: FinanceDelegate.qml
+linenos: true
+---
+```
+</details>
+
+`FinanceDelegate.qml` contains the following components:
+
+1. **ItemDelegate**:
+ - The root element of the delegate.
+ - Represents a single item in the `ListView`.
+
+2. **RowLayout**:
+ - A layout that arranges its children horizontally.
+ - Contains multiple elements to display different parts of the financial data.
+
+3. **Label (dateLabel)**:
+ - Displays the date of the expense.
+
+4. **ColumnLayout**:
+ - A layout that arranges its children vertically.
+ - Contains labels for the item name and category.
+
+5. **Label (item_name)**:
+ - Displays the name of the item.
+
+6. **Label (category)**:
+ - Displays the category of the transaction.
+
+7. **Item**:
+ - A spacer item to take up the remaining space in the `RowLayout` so that the last label is
+ aligned to the right.
+
+8. **ColumnLayout (cost section)**:
+ - A layout that arranges its children vertically.
+ - Contains labels for the cost description and the actual cost.
+
+9. **Label ("you spent:")**:
+ - Displays the static text "you spent:"
+
+10. **Label (cost)**:
+ - Displays the cost of the transaction.
+
+These components together create a detailed and structured visual representation of each financial
+transaction in the `ListView`, displaying the date, item name, category, and cost in a readable format.
+
+Then we create the `FinancePieChart.qml` file:
+
+<details>
+<summary class="prominent-summary">FinancePieChart.qml</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part1/Finance/FinancePieChart.qml
+---
+language: QML
+caption: FinancePieChart.qml
+linenos: true
+---
+```
+</details>
+
+`FinancePieChart.qml` contains the following components:
+
+1. **Item**:
+ - The root element of the QML file.
+ - Sets the width and height to match the screen dimensions.
+
+2. **GraphsView**:
+ - A container for displaying charts. This was introduced with
+ Qt 6.8 with Qt Graphs module.
+
+3. **PieSeries**:
+ - A series type for creating pie charts. This is also a part of the
+ Qt Graphs module.
+
+4. **Text**
+ - A title for the pie chart.
+
+5. **updateChart(data)**:
+ - A JavaScript function to update the pie chart with new data.
+ - Clears existing slices in the PieSeries.
+ - Iterates over the provided data to create new slices.
+ - Each slice is labeled with the category name and value in euros.
+
+These components together create a responsive pie chart that can be dynamically updated with new
+data.
+
+Finally, we create the `AddDialog.qml` file:
+
+<details>
+<summary class="prominent-summary">AddDialog.qml</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part1/Finance/AddDialog.qml
+---
+language: QML
+caption: AddDialog.qml
+linenos: true
+---
+```
+</details>
+
+`AddDialog.qml` contains the following components:
+
+1. **Dialog**:
+ - Root element for the dialog.: Identifier for the dialog.
+ - `signal finished(...)`: Custom signal emitted when the dialog is accepted. In this case, it
+ is emitted when the user adds a new expense.
+
+3. **ColumnLayout**:
+ - Container for the dialog fields.
+
+4. **TextField**:
+ - Input fields for the item name, category, cost and date.
+
+5. **Function `createEntry()`**:
+ - Clears the form fields.
+ - Sets the dialog title.
+ - Opens the dialog.
+
+6. **Dialog Properties**:
+ - `title: qsTr("Add Finance Item")`: Sets the dialog title.
+ - `standardButtons: Dialog.Ok | Dialog.Cancel`: Adds standard OK and Cancel buttons.
+ - `Component.onCompleted`: Hides the dialog when the component is first completed.
+ - `onAccepted`: Calls the `finished` function with the form data when the dialog is accepted.
+
+7. **Function `adjustDialogPosition`**:
+ - Adjusts the dialog position to move slightly up when the virtual keyboard is shown. This is
+ only applicable for mobile platforms.
+
+### Main Python file
+
+Now that we have created the main QML file and the necessary components, we can run the application
+to see the expense list view in action. Create a new Python file `main.py` and add the following
+code:
+
+<details>
+<summary class="prominent-summary">main.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part1/main.py
+language: python
+caption: main.py
+linenos: true
+---
+```
+</details>
+
+In the `main.py` file, we create a `QApplication` instance, load the `Main.qml` file. The Python
+import statement `from financemodel import FinanceModel` registers the `FinanceModel` class as a QML
+type, allowing it to be used in QML files.
+
+### Running the Application
+
+To run the application, execute the `main.py` file using Python:
+
+```bash
+python main.py
+```
+
+### Deploying the Application
+
+To deploy the application on Desktop, you can use the {ref}`pyside6-deploy` tool. Run the following
+command from the project directory:
+
+```bash
+pyside6-deploy --name FinanceManager
+```
+This will create a standalone executable for the application in the project directory.
+
+For deploying to Android, you can use the {ref}`pyside6-android-deploy` tool. Run the following
+command from the project directory:
+
+```bash
+pyside6-android-deploy --name FinanceManager --wheel-pyside=<path_to_pyside6_wheel>
+ --wheel-shiboken=<path_to_shiboken_wheel>
+```
+This will create an APK file that can be installed on an Android device in the project directory.
+
+## Summary
+
+In this part of the tutorial, we have created a basic finance manager app, including the expense
+list view categorized by month and year, the pie chart, and the add expense dialog. We have also
+defined the `FinanceModel` class in Python to manage the financial data and expose it to QML.
+In the next part of the tutorial, we will continue to build on this foundation by moving the
+expense data into a database based on the [`sqlalchemy`](https://www.sqlalchemy.org/) Python
+package.
--- /dev/null
+(tutorial_financemanager_parttwo)=
+
+# Finance Manager Tutorial - Part 2
+
+In this part of the tutorial, we will extend our finance manager app to store the expenses in a
+SQLite database using the [SQLAlchemy] Python package. This will allow us to
+persist the data and retrieve it even after the application is closed.
+
+To download the complete source code for this tutorial, visit
+{ref}`example_tutorials_finance_manager_part2`.
+
+## Prerequisites
+
+Before we begin, make sure you have [SQLAlchemy] installed within your Python environment.
+
+You can install it using pip:
+
+```bash
+pip install sqlalchemy
+```
+
+## Project Structure
+
+The overall project structure is the same as in the [previous part](tutorial_financemanager_partone)
+of the tutorial.
+
+## Let's Get Started!
+
+### Creating the Database
+
+The first step is to create a `database.py` that takes care of creating and initializing the
+database. This Python code will define the database schema and provide a session object to interact
+with the database. The database is initialized with a single table named `finances` with the same
+initial data we used in the [part one](tutorial_financemanager_partone) of the tutorial.
+
+<details>
+<summary class="prominent-summary">database.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part2/database.py
+---
+language: python
+caption: database.py
+linenos: true
+---
+```
+</details>
+
+> Note: The database called `finances.db` will be created in the directory specified by the
+`FINANCE_MANAGER_DB_PATH` environment variable if it is set. If the environment variable is not set,
+the database will be created in the appropriate application data directory based on the operating
+system.
+
+### Updating the FinanceModel Class
+
+Next, we need to update the FinanceModel class to interact with the database.
+
+In `financemodel.py`, make the following highlighted changes to load the existing expenses from the
+database into the model, and also save new expenses to the database.
+
+<details>
+<summary class="prominent-summary">financemodel.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part2/financemodel.py
+---
+language: python
+caption: Finance model class definition
+linenos: true
+emphasize-lines: 12, 40-50, 93-101
+---
+```
+
+## Updating the Main Application
+
+Finally, we need to update the `main.py` file to initialize the database and use the `FinanceModel`
+
+<details>
+<summary class="prominent-summary">main.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part2/main.py
+---
+language: python
+caption: main.py
+linenos: true
+emphasize-lines: 11, 15
+---
+```
+</details>
+
+The rest of the code remains the same as in the previous part of the tutorial.
+
+### Running the Application
+
+To run the application, execute the `main.py` file using Python:
+
+```bash
+python main.py
+```
+
+### Deploying the Application
+
+To deploy the application, follow the same steps as in the
+[previous part](tutorial_financemanager_partone) of the tutorial.
+
+## Summary
+
+In this part of the tutorial, we have extended the finance manager app by integrating a database
+using [SQLAlchemy]. This allows us to store the expenses and retrieve them even after the
+application is closed.
+
+In the next part of the tutorial, we will continue to enhance the application by using
+[FastApi] and [PyDantic] to create a REST API for the finance manager app, and move the
+database to a separate server.
+
+[SQLalchemy]: https://www.sqlalchemy.org/
+[FastApi]: https://fastapi.tiangolo.com/
+[PyDantic]: https://pydantic-docs.helpmanual.io/
--- /dev/null
+(tutorial_financemanager_partthree)=
+
+# Finance Manager Tutorial - Part 3
+
+In this part of the tutorial, we will extend our finance manager app to create a REST API using
+[FastAPI] and [Uvicorn]. This will allow us to perform server-side operations and interact with the
+SQLite database created in Part 2 using [SQLAlchemy].
+
+[FastAPI] is a modern, fast (high-performance), web framework for building APIs with Python.
+It is built on top of [ASGI (Asynchronous Server Gateway Interface)], which allows for efficient
+handling of concurrent requests.
+
+[Uvicorn] is a lightning-fast ASGI server implementation, using uvloop and httptools. It provides
+a high-performance server for running FastAPI applications.
+
+
+To download the complete source code for this tutorial, visit
+{ref}`example_tutorials_finance_manager_part3`.
+
+## Prerequisites
+
+Before we begin, make sure you have [FastAPI] and [Uvicorn] installed within your
+Python environment.
+
+You can install them using pip:
+
+```bash
+pip install fastapi uvicorn
+```
+
+## Project Structure
+
+The overall project structure in this part of the tutorial is much different from the previous
+parts. We move the frontend part that uses PySide6 and QML to a separate directory called `Frontend`
+and the backend part that uses FastAPI to create a REST API to the SQLite database to a directory
+called `Backend`.
+
+```
+├── Backend
+│ ├── database.py
+│ ├── main.py
+│ └── rest_api.py
+├── Frontend
+│ ├── Finance
+│ │ ├── AddDialog.qml
+│ │ ├── FinanceDelegate.qml
+│ │ ├── FinancePieChart.qml
+│ │ ├── FinanceView.qml
+│ │ ├── Main.qml
+│ │ └── qmldir
+│ ├── financemodel.py
+│ └── main.py
+```
+
+## Let's Get Started!
+
+First, let's create the `Backend` directory and move the `database.py` from the
+[previous part](tutorial_financemanager_parttwo) to the `Backend` directory.
+
+### Backend Setup
+
+### Setting Up FastAPI
+
+Create a new Python file `rest_api.py` in the `Backend` directory and add the following code:
+
+<details>
+<summary class="prominent-summary">rest_api.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part3/Backend/rest_api.py
+---
+language: python
+caption: rest_api.py
+linenos: true
+---
+```
+</details>
+
+In `rest_api.py`, we set up a FastAPI application to handle Create and Read operations for the
+finance data. The file includes:
+
+1. **FastAPI Application**: Initializes a FastAPI instance.
+2. **Pydantic Models**: Defines `FinanceCreate` for input validation and creating new records
+and `FinanceRead` for output formatting. `FinanceRead` includes additional configuration for
+ORM models.
+3. **Database Dependency**: Provides a `get_db` function to manage database sessions.
+4. **Create Endpoint**: A POST endpoint `/finances/` to add new finance entries to the database.
+5. **Read Endpoint**: A GET endpoint `/finances/` to retrieve finance entries from the database.
+
+This setup allows the application to interact with the database, enabling the creation and retrieval
+of finance records via RESTful API endpoints.
+
+### Creating a main Python File for the Backend
+
+Create a new Python file `main.py` in the `Backend` directory and add the following code:
+
+<details>
+<summary class="prominent-summary">main.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part3/Backend/main.py
+---
+language: python
+caption: main.py
+linenos: true
+---
+```
+</details>
+
+In `main.py`, along with initializing the database we create a [Uvicorn] server instance to run the
+FastAPI application. The server runs on 127.0.0.1 at port 8000 and reloads automatically when code
+changes are detected.
+
+### Frontend Setup
+
+Then we move on to the frontend part of the application, and connect it to the FastAPI backend.
+We will create a new directory `Frontend`. Move the folder `Finance` and the files `financemodel.py`
+and `main.py` from the previous parts to the `Frontend` directory.
+
+Most of the code remains the same as in the previous parts, with minor changes to connect to the
+REST API.
+
+### Updating the FinanceModel Class
+
+The following changes (highlighted) are made to the `financemodel.py` file which removes
+the database interaction code from Python and adds the REST API interaction code.
+
+<details>
+<summary class="prominent-summary">financemodel.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part3/Frontend/financemodel.py
+---
+language: python
+caption: financemodel.py
+linenos: true
+emphasize-lines: 39-61, 104-114
+---
+```
+</details>
+
+Two methods are overridden for the `FinanceModel` class - `fetchMore` and `canFetchMore`. These
+methods are used to fetch more data from the REST API when the model is scrolled to the end. The data
+is fetched in chunks of 10 entries at a time. Additionally, the `append` method is updated to send a
+POST request to the REST API to add a new finance entry.
+
+### Updating the Main Python File for the Frontend
+
+Finally, we update the `main.py` file in the `Frontend` directory to remove the database
+initialization code.
+
+<details>
+<summary class="prominent-summary">main.py</summary>
+
+```{literalinclude} ../../../../../../../../../examples/tutorials/finance_manager/part3/Frontend/main.py
+---
+language: python
+caption: main.py
+linenos: true
+---
+```
+</details>
+
+The rest of the code and the QML files remains the same as in the previous parts of the tutorial.
+
+### Running the Application
+
+To run the application, first start the backend FastAPI server by executing the `main.py` file in
+the `Backend` directory using Python:
+
+```bash
+python Backend/main.py
+```
+
+This will start the Uvicorn server running the FastAPI application on `http://127.0.0.1:8000`.
+
+After starting the backend server, run the frontend application by executing the `main.py` file in
+the `Frontend` directory using Python:
+
+```bash
+python Frontend/main.py
+```
+
+This will start the PySide6 application that connects to the FastAPI backend to display the finance
+data.
+
+## Deployment
+
+### Deploying the Application
+
+To deploy the application, follow the same steps as in the
+[first part](tutorial_financemanager_partone) of the tutorial.
+
+## Summary
+
+At the end of this tutorial, you have extended the finance manager app to include a REST API using
+FastAPI and Uvicorn. The backend application interacts with the SQLite database using SQLAlchemy
+and provides endpoints to create and retrieve finance records. The frontend application connects to
+the backend using the REST API to display the finance data.
+
+If you want to extend the application further, you can add more features like updating and deleting
+finance records, adding user authentication etc.
+
+[FastAPI]: https://fastapi.tiangolo.com/
+[Uvicorn]: https://www.uvicorn.org/
+[PyDantic]: https://pydantic-docs.helpmanual.io/
+[SQAlchemy]: https://www.sqlalchemy.org/
+.. _pyside6_tutorials:
+
Tutorials
=========
.. grid-item-card:: Basic Widget
:class-item: cover-img
- :link: basictutorial/widgets.html
+ :link: tutorial_widgets
+ :link-type: ref
:img-top: basictutorial/widgets.png
Your first QtWidgets Application
.. grid-item-card:: Basic Button
:class-item: cover-img
- :link: basictutorial/clickablebutton.html
+ :link: tutorial_clickablebutton
+ :link-type: ref
:img-top: basictutorial/clickablebutton.png
Using a Simple Button
.. grid-item-card:: Basic Connections
:class-item: cover-img
- :link: basictutorial/signals_and_slots.html
+ :link: tutorial_signals_and_slots
+ :link-type: ref
:img-top: basictutorial/signals_slots.png
Signals and Slots
.. grid-item-card:: Basic Dialog
:class-item: cover-img
- :link: basictutorial/dialog.html
+ :link: tutorial_dialog
+ :link-type: ref
:img-top: basictutorial/dialog.png
Creating a Dialog Application
.. grid-item-card:: Basic Table
:class-item: cover-img
- :link: basictutorial/tablewidget.html
+ :link: tutorial_tablewidget
+ :link-type: ref
:img-top: basictutorial/tablewidget.png
Displaying Data Using a Table Widget
.. grid-item-card:: Basic Tree
:class-item: cover-img
- :link: basictutorial/treewidget.html
+ :link: tutorial_treewidget
+ :link-type: ref
:img-top: basictutorial/treewidget.png
Displaying Data Using a Tree Widget
.. grid-item-card:: Basic ``ui`` files
:class-item: cover-img
- :link: basictutorial/uifiles.html
+ :link: tutorial_uifiles
+ :link-type: ref
:img-top: basictutorial/uifiles.png
Using .ui files from Designer or QtCreator with QUiLoader and pyside6-uic
.. grid-item-card:: Basic ``qrc`` files
:class-item: cover-img
- :link: basictutorial/qrcfiles.html
+ :link: tutorial_qrcfiles
+ :link-type: ref
:img-top: basictutorial/player-new.png
Using .qrc Files (pyside6-rcc)
.. grid-item-card:: Basic Translations
:class-item: cover-img
- :link: basictutorial/translations.html
+ :link: tutorial_translations
+ :link-type: ref
:img-top: basictutorial/translations.png
Translating Applications
.. grid-item-card:: Basic Widget Style
:class-item: cover-img
- :link: basictutorial/widgetstyling.html
+ :link: tutorial_widgetstyling
+ :link-type: ref
:img-top: basictutorial/widgetstyling-yes.png
Styling the Widgets Application
.. grid-item-card:: Basic Quick
:class-item: cover-img
- :link: basictutorial/qml.html
+ :link: tutorial_qml
+ :link-type: ref
:img-top: basictutorial/greenapplication.png
Your First QtQuick/QML Application
.. grid-item-card:: Basic QML Integration
:class-item: cover-img
- :link: qmlintegration/qmlintegration.html
+ :link: tutorial_qmlintegration
+ :link-type: ref
:img-top: qmlintegration/textproperties_material.png
Python-QML integration
.. grid-item-card:: QML Application
:class-item: cover-img
- :link: qmlapp/qmlapplication.html
+ :link: tutorial_qmlapplication
+ :link-type: ref
:img-top: qmlapp/qmlapplication.png
QML Application Tutorial (QtCreator)
.. grid-item-card:: Advanced QML Integration
:class-item: cover-img
- :link: qmlsqlintegration/qmlsqlintegration.html
+ :link: tutorial_qmlsqlintegration
+ :link-type: ref
:img-top: qmlsqlintegration/example_list_view.png
QML, SQL and PySide Integration Tutorial
.. grid-item-card:: Extended Explorer
:class-item: cover-img
- :link: extendedexplorer/extendedexplorer.html
+ :link: tutorial_extendedexplorer
+ :link-type: ref
:img-top: extendedexplorer/resources/extendedexplorer.webp
Extending an Qt Quick Controls example
+ .. grid-item-card:: Finance Manager Tutorial
+ :class-item: cover-img
+ :link: tutorial_financemanager
+ :link-type: ref
+ :img-top: finance_manager/part1/resources/finance_manager.webp
+
+ Finance Manager Tutorial using QtQuick, SQLAlchemy, and FastAPI demonstrating how
+ PySide6 can be used to interact with other popular packages in the Python ecosystem.
+
.. toctree::
:maxdepth: 1
:hidden:
qmlapp/qmlapplication.rst
qmlsqlintegration/qmlsqlintegration.rst
extendedexplorer/extendedexplorer.md
+ finance_manager/index.md
General Applications
--------------------
.. grid-item-card:: Data Visualization
:class-item: cover-img
- :link: datavisualize/index.html
+ :link: datavisualize_index
+ :link-type: ref
:img-top: datavisualize/images/datavisualization_app.png
Data Visualization Tool
.. grid-item-card:: Expenses Application
:class-item: cover-img
- :link: expenses/expenses.html
+ :link: tutorial_expenses
+ :link-type: ref
:img-top: expenses/expenses_tool.png
Expenses administration tool
+ .. grid-item-card:: Deploying PySide6 Applications to Boot to Qt
+ :class-item: cover-img
+ :link: tutorial_boottoqt_raspberrypi
+ :link-type: ref
+ :img-top: embedded/boot2qt_launcher.png
+
+ Deploying PySide6 Applications to Boot to Qt OS on Raspberry Pi
+
.. toctree::
:hidden:
datavisualize/index.rst
expenses/expenses.rst
+ embedded/boot2qt.md
Qt Overviews
------------
.. toctree::
:maxdepth: 1
- ../overviews/overviews-main.rst
+ ../overviews/qtdoc-overviews-main.rst
C++ and Python
--------------
debugging/mixed_debugging.rst
debugging/qml_debugging.rst
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtSql import QSqlDatabase, QSqlError, QSqlQuery
from datetime import date
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import copy
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtSql import QSqlDatabase, QSqlError, QSqlQuery
from datetime import date
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import copy
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import copy, os
from PySide6.QtSql import QSqlRelationalDelegate
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtGui import QAction
from PySide6.QtWidgets import (QAbstractItemView, QDataWidgetMapper,
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtSql import QSqlDatabase, QSqlError, QSqlQuery
from datetime import date
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtWidgets import QApplication
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
from PySide6.QtWidgets import QApplication
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
import random
Before you start, ensure that all the prerequisites for
Qt for Python are met. See
-:doc:`Getting Started <../../gettingstarted/index>` for more
+:doc:`Getting Started <../../gettingstarted>` for more
information. In addition, familiarize yourself with the
basic differences between Qt in C++ and in Python.
name without the ``global`` statement. This would lead to
a ``NameError`` in the ``value is None`` handling, on
accessing ``var``. For more information about this, see
-`Python refernce documentation <python refdoc>`_.
+`Python reference documentation <python-refdoc_>`_.
-.. _python refdoc: https://docs.python.org/3/reference/simple_stmts.html#the-global-statement
+.. _python-refdoc: https://docs.python.org/3/reference/simple_stmts.html#the-global-statement
.. tip:: Python being an interpreted language, most often
- the easiest way is to try your idea in the interperter.
+ the easiest way is to try your idea in the interpreter.
You could call the ``help()`` function in the
interpreter on any built-in function or keyword in
Python. For example, a call to ``help(import)`` should
- provide documentation about the ``import`` statment
+ provide documentation about the ``import`` statement
Last but not the least, try out a few examples to
familiarize yourself with the Python coding style and
follow the guidelines outlined in the
-`PEP8 - Style Guide <pep8>`_.
+`PEP8 - Style Guide <pep8_>`_.
-.. _pep8: <https://www.python.org/dev/peps/pep-0008/#naming-conventions>
+.. _pep8: https://www.python.org/dev/peps/pep-0008/#naming-conventions
.. code-block:: python
--- /dev/null
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+
+Page {
+ width: 640
+ height: 480
+ required property var myModel
+
+ header: Label {
+ color: "#15af15"
+ text: qsTr("Where do people use Qt?")
+ font.pointSize: 17
+ font.bold: true
+ font.family: "Arial"
+ renderType: Text.NativeRendering
+ horizontalAlignment: Text.AlignHCenter
+ padding: 10
+ }
+ Rectangle {
+ id: root
+ width: parent.width
+ height: parent.height
+
+ Image {
+ id: image
+ fillMode: Image.PreserveAspectFit
+ anchors.centerIn: root
+ source: "./logo.png"
+ opacity: 0.5
+
+ }
+
+ ListView {
+ id: view
+ anchors.fill: root
+ anchors.margins: 25
+ model: myModel
+ delegate: Text {
+ anchors.leftMargin: 50
+ font.pointSize: 15
+ horizontalAlignment: Text.AlignHCenter
+ text: display
+ }
+ }
+ }
+ NumberAnimation {
+ id: anim
+ running: true
+ target: view
+ property: "contentY"
+ duration: 500
+ }
+}
--- /dev/null
+module App
+Main 254.0 Main.qml
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
import urllib.request
import json
-from pathlib import Path
from PySide6.QtQuick import QQuickView
-from PySide6.QtCore import QStringListModel, QUrl
+from PySide6.QtCore import QStringListModel
from PySide6.QtGui import QGuiApplication
if __name__ == '__main__':
- #get our data
+ # get our data
url = "http://country.io/names.json"
response = urllib.request.urlopen(url)
data = json.loads(response.read().decode('utf-8'))
- #Format and sort the data
+ # Format and sort the data
data_list = list(data.values())
data_list.sort()
- #Set up the application window
+ # Set up the application window
app = QGuiApplication(sys.argv)
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView)
- #Expose the list to the Qml code
+ # Expose the list to the Qml code
my_model = QStringListModel()
my_model.setStringList(data_list)
view.setInitialProperties({"myModel": my_model})
- #Load the QML file
- qml_file = Path(__file__).parent / "view.qml"
- view.setSource(QUrl.fromLocalFile(qml_file.resolve()))
+ # Load the QML file
+ # Add the current directory to the import paths and load the main module.
+ view.engine().addImportPath(sys.path[0])
+ view.loadFromModule("App", "Main")
- #Show the window
+ # Show the window
if view.status() == QQuickView.Error:
sys.exit(-1)
view.show()
- #execute and cleanup
+ # execute and cleanup
app.exec()
del view
+.. _tutorial_qmlapplication:
+
#########################
QML Application Tutorial
#########################
This should create a ``main.py`` and ```main.pyproject`` files
for the project.
-#. Download :download:`view.qml<view.qml>` and :download:`logo.png <logo.png>`
- and move them to your project folder.
+#. Download :download:`Main.qml<App/Main.qml>`, :download:`qmldir<App/qmldir>`
+ and :download:`logo.png <App/logo.png>` and place them in a subdirectory
+ named `App` in your project folder. This creates a basic QML module.
#. Double-click on ``main.pyproject`` to open it in edit mode, and append
``view.qml`` and ``logo.png`` to the **files** list. This is how your
.. code::
{
- "files": ["main.py", "view.qml", "logo.png"]
+ "files": ["main.py", "App/Main.qml", "App/logo.png", "App/qmldir"]
}
#. Now that you have the necessary bits for the application, import the
.. literalinclude:: main.py
:linenos:
- :lines: 3-23
- :emphasize-lines: 7-9,14-17
+ :lines: 5-23
+ :emphasize-lines: 5-7,12-15
#. Now, set up the application window using
:ref:`PySide6.QtGui.QGuiApplication<qguiapplication>`, which manages the application-wide
.. literalinclude:: main.py
:linenos:
- :lines: 3-28
- :emphasize-lines: 23-25
+ :lines: 5-28
+ :emphasize-lines: 21-24
.. note:: Setting the resize policy is important if you want the
root item to resize itself to fit the window or vice-a-versa.
.. literalinclude:: main.py
:linenos:
- :lines: 3-33
- :emphasize-lines: 28-31
+ :lines: 5-33
+ :emphasize-lines: 26-29
-#. Load the ``view.qml`` to the ``QQuickView`` and call ``show()`` to
+#. Load the ``Main.qml`` to the ``QQuickView`` and call ``show()`` to
display the application window.
.. literalinclude:: main.py
:linenos:
- :lines: 3-42
- :emphasize-lines: 33-40
+ :lines: 5-43
+ :emphasize-lines: 31-39
#. Finally, execute the application to start the event loop and clean up.
.. literalinclude:: main.py
:linenos:
- :lines: 3-
- :emphasize-lines: 42-44
+ :lines: 5-
+ :emphasize-lines: 41-43
#. Your application is ready to be run now. Select **Projects** mode to
choose the Python version to run it.
Related information
********************
-* `QML Reference <https://doc.qt.io/qt-5/qmlreference.html>`_
+* `QML Reference <https://doc.qt.io/qt-6/qmlreference.html>`_
* :doc:`../qmlintegration/qmlintegration`
+++ /dev/null
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-import QtQuick 2.12
-import QtQuick.Controls 2.12
-
-Page {
- width: 640
- height: 480
- required property var myModel
-
- header: Label {
- color: "#15af15"
- text: qsTr("Where do people use Qt?")
- font.pointSize: 17
- font.bold: true
- font.family: "Arial"
- renderType: Text.NativeRendering
- horizontalAlignment: Text.AlignHCenter
- padding: 10
- }
- Rectangle {
- id: root
- width: parent.width
- height: parent.height
-
- Image {
- id: image
- fillMode: Image.PreserveAspectFit
- anchors.centerIn: root
- source: "./logo.png"
- opacity: 0.5
-
- }
-
- ListView {
- id: view
- anchors.fill: root
- anchors.margins: 25
- model: myModel
- delegate: Text {
- anchors.leftMargin: 50
- font.pointSize: 15
- horizontalAlignment: Text.AlignHCenter
- text: display
- }
- }
- }
- NumberAnimation {
- id: anim
- running: true
- target: view
- property: "contentY"
- duration: 500
- }
-}
--- /dev/null
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+
+import QtQuick 2.0
+import QtQuick.Layouts 1.11
+import QtQuick.Controls 2.1
+import QtQuick.Window 2.1
+import QtQuick.Controls.Material 2.1
+
+import io.qt.textproperties 1.0
+
+ApplicationWindow {
+ id: page
+ width: 800
+ height: 400
+ visible: true
+ Material.theme: Material.Dark
+ Material.accent: Material.Red
+
+ Bridge {
+ id: bridge
+ }
+
+ GridLayout {
+ id: grid
+ columns: 2
+ rows: 3
+
+ ColumnLayout {
+ spacing: 2
+ Layout.columnSpan: 1
+ Layout.preferredWidth: 400
+
+ Text {
+ id: leftlabel
+ Layout.alignment: Qt.AlignHCenter
+ color: "white"
+ font.pointSize: 16
+ text: "Qt for Python"
+ Layout.preferredHeight: 100
+ Material.accent: Material.Green
+ }
+
+ RadioButton {
+ id: italic
+ Layout.alignment: Qt.AlignLeft
+ text: "Italic"
+ onToggled: {
+ leftlabel.font.italic = bridge.getItalic(italic.text)
+ leftlabel.font.bold = bridge.getBold(italic.text)
+ leftlabel.font.underline = bridge.getUnderline(italic.text)
+
+ }
+ }
+ RadioButton {
+ id: bold
+ Layout.alignment: Qt.AlignLeft
+ text: "Bold"
+ onToggled: {
+ leftlabel.font.italic = bridge.getItalic(bold.text)
+ leftlabel.font.bold = bridge.getBold(bold.text)
+ leftlabel.font.underline = bridge.getUnderline(bold.text)
+ }
+ }
+ RadioButton {
+ id: underline
+ Layout.alignment: Qt.AlignLeft
+ text: "Underline"
+ onToggled: {
+ leftlabel.font.italic = bridge.getItalic(underline.text)
+ leftlabel.font.bold = bridge.getBold(underline.text)
+ leftlabel.font.underline = bridge.getUnderline(underline.text)
+ }
+ }
+ RadioButton {
+ id: noneradio
+ Layout.alignment: Qt.AlignLeft
+ text: "None"
+ checked: true
+ onToggled: {
+ leftlabel.font.italic = bridge.getItalic(noneradio.text)
+ leftlabel.font.bold = bridge.getBold(noneradio.text)
+ leftlabel.font.underline = bridge.getUnderline(noneradio.text)
+ }
+ }
+ }
+
+ ColumnLayout {
+ id: rightcolumn
+ spacing: 2
+ Layout.columnSpan: 1
+ Layout.preferredWidth: 400
+ Layout.preferredHeight: 400
+ Layout.fillWidth: true
+
+ RowLayout {
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+
+
+ Button {
+ id: red
+ text: "Red"
+ highlighted: true
+ Material.accent: Material.Red
+ onClicked: {
+ leftlabel.color = bridge.getColor(red.text)
+ }
+ }
+ Button {
+ id: green
+ text: "Green"
+ highlighted: true
+ Material.accent: Material.Green
+ onClicked: {
+ leftlabel.color = bridge.getColor(green.text)
+ }
+ }
+ Button {
+ id: blue
+ text: "Blue"
+ highlighted: true
+ Material.accent: Material.Blue
+ onClicked: {
+ leftlabel.color = bridge.getColor(blue.text)
+ }
+ }
+ Button {
+ id: nonebutton
+ text: "None"
+ highlighted: true
+ Material.accent: Material.BlueGrey
+ onClicked: {
+ leftlabel.color = bridge.getColor(nonebutton.text)
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+ Text {
+ id: rightlabel
+ color: "white"
+ Layout.alignment: Qt.AlignLeft
+ text: "Font size"
+ Material.accent: Material.White
+ }
+ Slider {
+ width: rightcolumn.width*0.6
+ Layout.alignment: Qt.AlignRight
+ id: slider
+ value: 0.5
+ onValueChanged: {
+ leftlabel.font.pointSize = bridge.getSize(value)
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+module QmlIntegration
+Main 254.0 Main.qml
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
-from pathlib import Path
from PySide6.QtCore import QObject, Slot
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine, QmlElement
from PySide6.QtQuickControls2 import QQuickStyle
-import style_rc
+import rc_style # noqa F401
# To be used on the @QmlElement decorator
# (QML_IMPORT_MINOR_VERSION is optional)
def getColor(self, s):
if s.lower() == "red":
return "#ef9a9a"
- elif s.lower() == "green":
+ if s.lower() == "green":
return "#a5d6a7"
- elif s.lower() == "blue":
+ if s.lower() == "blue":
return "#90caf9"
- else:
- return "white"
+ return "white"
@Slot(float, result=int)
def getSize(self, s):
size = int(s * 34)
- if size <= 0:
- return 1
- else:
- return size
+ return max(1, size)
@Slot(str, result=bool)
def getItalic(self, s):
- if s.lower() == "italic":
- return True
- else:
- return False
+ return s.lower() == "italic"
@Slot(str, result=bool)
def getBold(self, s):
- if s.lower() == "bold":
- return True
- else:
- return False
+ return s.lower() == "bold"
@Slot(str, result=bool)
def getUnderline(self, s):
- if s.lower() == "underline":
- return True
- else:
- return False
+ return s.lower() == "underline"
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
QQuickStyle.setStyle("Material")
engine = QQmlApplicationEngine()
-
- # Get the path of the current directory, and then add the name
- # of the QML file, to load it.
- qml_file = Path(__file__).parent / 'view.qml'
- engine.load(qml_file)
+ # Add the current directory to the import paths and load the main module.
+ engine.addImportPath(sys.path[0])
+ engine.loadFromModule("QmlIntegration", "Main")
if not engine.rootObjects():
sys.exit(-1)
- sys.exit(app.exec())
+ exit_code = app.exec()
+ del engine
+ sys.exit(exit_code)
+.. _tutorial_qmlintegration:
+
Python-QML integration
======================
.. literalinclude:: main.py
:linenos:
- :lines: 63-76
- :emphasize-lines: 4,9
+ :lines: 51-64
+ :emphasize-lines: 6,7
Notice that we only need a :code:`QQmlApplicationEngine` to
:code:`load` the QML file.
.. literalinclude:: main.py
:linenos:
- :lines: 14-54
+ :lines: 14-49
:emphasize-lines: 3,4,7
Notice that the registration happens thanks to the :code:`QmlElement`
This :code:`id` will help you to get a reference to the element
that was registered from Python.
- .. literalinclude:: view.qml
+ .. literalinclude:: QmlIntegration/Main.qml
:linenos:
:lines: 45-55
:emphasize-lines: 6-8
will return *False*, that is how we make sure only one is being
applied to the text.
+#. Put the file into into a directory named :code:`QmlIntegration` along
+ with a file named :code:`qmldir` to describe a basic QML module:
+
+ .. code-block:: text
+
+ module QmlIntegration
+ Main 254.0 Main.qml
+
#. Each slot verifies if the selected option contains the text associated
to the property:
.. literalinclude:: main.py
:linenos:
- :lines: 42-47
- :emphasize-lines: 4,6
+ :lines: 42-44
Returning *True* or *False* allows you to activate and deactivate
the properties of the QML UI elements.
.. literalinclude:: main.py
:linenos:
- :lines: 34-39
+ :lines: 33-36
#. Now, for changing the look of our application, you have two options:
.. literalinclude:: style.qrc
:linenos:
- Generate the *rc* file running, ``pyside6-rcc style.qrc -o style_rc.py``
+ Generate the *rc* file running, ``pyside6-rcc style.qrc -o rc_style.py``
And finally import it from your ``main.py`` script.
.. literalinclude:: main.py
:linenos:
- :lines: 4-12
- :emphasize-lines: 9
+ :lines: 5-12
+ :emphasize-lines: 8
You can read more about this configuration file
`here <https://doc.qt.io/qt-5/qtquickcontrols2-configuration.html>`_.
.. image:: textproperties_material.png
-You can :download:`view.qml <view.qml>` and
-:download:`main.py <main.py>` to try this example.
+You can download :download:`Main.qml <QmlIntegration/Main.qml>`,
+:download:`qmldir <QmlIntegration/qmldir>` and :download:`main.py <main.py>`
+to try this example.
+++ /dev/null
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-
-import QtQuick 2.0
-import QtQuick.Layouts 1.11
-import QtQuick.Controls 2.1
-import QtQuick.Window 2.1
-import QtQuick.Controls.Material 2.1
-
-import io.qt.textproperties 1.0
-
-ApplicationWindow {
- id: page
- width: 800
- height: 400
- visible: true
- Material.theme: Material.Dark
- Material.accent: Material.Red
-
- Bridge {
- id: bridge
- }
-
- GridLayout {
- id: grid
- columns: 2
- rows: 3
-
- ColumnLayout {
- spacing: 2
- Layout.columnSpan: 1
- Layout.preferredWidth: 400
-
- Text {
- id: leftlabel
- Layout.alignment: Qt.AlignHCenter
- color: "white"
- font.pointSize: 16
- text: "Qt for Python"
- Layout.preferredHeight: 100
- Material.accent: Material.Green
- }
-
- RadioButton {
- id: italic
- Layout.alignment: Qt.AlignLeft
- text: "Italic"
- onToggled: {
- leftlabel.font.italic = bridge.getItalic(italic.text)
- leftlabel.font.bold = bridge.getBold(italic.text)
- leftlabel.font.underline = bridge.getUnderline(italic.text)
-
- }
- }
- RadioButton {
- id: bold
- Layout.alignment: Qt.AlignLeft
- text: "Bold"
- onToggled: {
- leftlabel.font.italic = bridge.getItalic(bold.text)
- leftlabel.font.bold = bridge.getBold(bold.text)
- leftlabel.font.underline = bridge.getUnderline(bold.text)
- }
- }
- RadioButton {
- id: underline
- Layout.alignment: Qt.AlignLeft
- text: "Underline"
- onToggled: {
- leftlabel.font.italic = bridge.getItalic(underline.text)
- leftlabel.font.bold = bridge.getBold(underline.text)
- leftlabel.font.underline = bridge.getUnderline(underline.text)
- }
- }
- RadioButton {
- id: noneradio
- Layout.alignment: Qt.AlignLeft
- text: "None"
- checked: true
- onToggled: {
- leftlabel.font.italic = bridge.getItalic(noneradio.text)
- leftlabel.font.bold = bridge.getBold(noneradio.text)
- leftlabel.font.underline = bridge.getUnderline(noneradio.text)
- }
- }
- }
-
- ColumnLayout {
- id: rightcolumn
- spacing: 2
- Layout.columnSpan: 1
- Layout.preferredWidth: 400
- Layout.preferredHeight: 400
- Layout.fillWidth: true
-
- RowLayout {
- Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
-
-
- Button {
- id: red
- text: "Red"
- highlighted: true
- Material.accent: Material.Red
- onClicked: {
- leftlabel.color = bridge.getColor(red.text)
- }
- }
- Button {
- id: green
- text: "Green"
- highlighted: true
- Material.accent: Material.Green
- onClicked: {
- leftlabel.color = bridge.getColor(green.text)
- }
- }
- Button {
- id: blue
- text: "Blue"
- highlighted: true
- Material.accent: Material.Blue
- onClicked: {
- leftlabel.color = bridge.getColor(blue.text)
- }
- }
- Button {
- id: nonebutton
- text: "None"
- highlighted: true
- Material.accent: Material.BlueGrey
- onClicked: {
- leftlabel.color = bridge.getColor(nonebutton.text)
- }
- }
- }
- RowLayout {
- Layout.fillWidth: true
- Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
- Text {
- id: rightlabel
- color: "white"
- Layout.alignment: Qt.AlignLeft
- text: "Font size"
- Material.accent: Material.White
- }
- Slider {
- width: rightcolumn.width*0.6
- Layout.alignment: Qt.AlignRight
- id: slider
- value: 0.5
- onValueChanged: {
- leftlabel.font.pointSize = bridge.getSize(value)
- }
- }
- }
- }
- }
-}
--- /dev/null
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import ChatModel
+
+ApplicationWindow {
+ id: window
+ title: qsTr("Chat")
+ width: 640
+ height: 960
+ visible: true
+
+ SqlConversationModel {
+ id: chat_model
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ ListView {
+ id: listView
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.margins: pane.leftPadding + messageField.leftPadding
+ displayMarginBeginning: 40
+ displayMarginEnd: 40
+ verticalLayoutDirection: ListView.BottomToTop
+ spacing: 12
+ model: chat_model
+ delegate: Column {
+ anchors.right: sentByMe ? listView.contentItem.right : undefined
+ spacing: 6
+
+ readonly property bool sentByMe: model.recipient !== "Me"
+ Row {
+ id: messageRow
+ spacing: 6
+ anchors.right: sentByMe ? parent.right : undefined
+
+ Rectangle {
+ width: Math.min(messageText.implicitWidth + 24,
+ listView.width - (!sentByMe ? messageRow.spacing : 0))
+ height: messageText.implicitHeight + 24
+ radius: 15
+ color: sentByMe ? "lightgrey" : "steelblue"
+
+ Label {
+ id: messageText
+ text: model.message
+ color: sentByMe ? "black" : "white"
+ anchors.fill: parent
+ anchors.margins: 12
+ wrapMode: Label.Wrap
+ }
+ }
+ }
+
+ Label {
+ id: timestampText
+ text: Qt.formatDateTime(model.timestamp, "d MMM hh:mm")
+ color: "lightgrey"
+ anchors.right: sentByMe ? parent.right : undefined
+ }
+ }
+
+ ScrollBar.vertical: ScrollBar {}
+ }
+
+ Pane {
+ id: pane
+ Layout.fillWidth: true
+
+ RowLayout {
+ width: parent.width
+
+ TextArea {
+ id: messageField
+ Layout.fillWidth: true
+ placeholderText: qsTr("Compose message")
+ wrapMode: TextArea.Wrap
+ }
+
+ Button {
+ id: sendButton
+ text: qsTr("Send")
+ enabled: messageField.length > 0
+ onClicked: {
+ listView.model.send_message("machine", messageField.text, "Me");
+ messageField.text = "";
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+module App
+Main 254.0 Main.qml
--- /dev/null
+module Main
+Main 254.0 Main.qml
+++ /dev/null
-// Copyright (C) 2021 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-import QtQuick
-import QtQuick.Layouts
-import QtQuick.Controls
-import ChatModel
-
-ApplicationWindow {
- id: window
- title: qsTr("Chat")
- width: 640
- height: 960
- visible: true
-
- SqlConversationModel {
- id: chat_model
- }
-
- ColumnLayout {
- anchors.fill: window
-
- ListView {
- id: listView
- Layout.fillWidth: true
- Layout.fillHeight: true
- Layout.margins: pane.leftPadding + messageField.leftPadding
- displayMarginBeginning: 40
- displayMarginEnd: 40
- verticalLayoutDirection: ListView.BottomToTop
- spacing: 12
- model: chat_model
- delegate: Column {
- anchors.right: sentByMe ? listView.contentItem.right : undefined
- spacing: 6
-
- readonly property bool sentByMe: model.recipient !== "Me"
- Row {
- id: messageRow
- spacing: 6
- anchors.right: sentByMe ? parent.right : undefined
-
- Rectangle {
- width: Math.min(messageText.implicitWidth + 24,
- listView.width - (!sentByMe ? messageRow.spacing : 0))
- height: messageText.implicitHeight + 24
- radius: 15
- color: sentByMe ? "lightgrey" : "steelblue"
-
- Label {
- id: messageText
- text: model.message
- color: sentByMe ? "black" : "white"
- anchors.fill: parent
- anchors.margins: 12
- wrapMode: Label.Wrap
- }
- }
- }
-
- Label {
- id: timestampText
- text: Qt.formatDateTime(model.timestamp, "d MMM hh:mm")
- color: "lightgrey"
- anchors.right: sentByMe ? parent.right : undefined
- }
- }
-
- ScrollBar.vertical: ScrollBar {}
- }
-
- Pane {
- id: pane
- Layout.fillWidth: true
-
- RowLayout {
- width: parent.width
-
- TextArea {
- id: messageField
- Layout.fillWidth: true
- placeholderText: qsTr("Compose message")
- wrapMode: TextArea.Wrap
- }
-
- Button {
- id: sendButton
- text: qsTr("Send")
- enabled: messageField.length > 0
- onClicked: {
- listView.model.send_message("machine", messageField.text, "Me");
- messageField.text = "";
- }
- }
- }
- }
- }
-}
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import sys
import logging
-from PySide6.QtCore import QDir, QFile, QUrl
+from PySide6.QtCore import QCoreApplication, QDir, QFile, QStandardPaths
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtSql import QSqlDatabase
# We import the file just to trigger the QmlElement type registration.
-import sqlDialog
+import sqlDialog # noqa E703
logging.basicConfig(filename="chat.log", level=logging.DEBUG)
logger = logging.getLogger("logger")
if not database.isValid():
logger.error("Cannot add database")
- write_dir = QDir("")
+ app_data = QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppDataLocation)
+ write_dir = QDir(app_data)
if not write_dir.mkpath("."):
- logger.error("Failed to create writable directory")
+ logger.error(f"Failed to create writable directory {app_data}")
# Ensure that we have a writable location on all devices.
abs_path = write_dir.absolutePath()
if __name__ == "__main__":
app = QGuiApplication()
+ QCoreApplication.setOrganizationName("QtProject")
+ QCoreApplication.setApplicationName("Chat Tutorial")
+
connectToDatabase()
engine = QQmlApplicationEngine()
- engine.load(QUrl("chat.qml"))
+ engine.addImportPath(sys.path[0])
+ engine.loadFromModule("App", "Main")
if not engine.rootObjects():
sys.exit(-1)
- app.exec()
+ exit_code = app.exec()
+ del engine
+ sys.exit(exit_code)
+.. _tutorial_qmlsqlintegration:
+
QML, SQL and PySide Integration Tutorial
########################################
.. literalinclude:: sqlDialog.py
:linenos:
- :lines: 4-43
+ :lines: 5-44
The ``SqlConversationModel`` class offers the read-only data model required for the non-editable
contacts list. It derives from the :ref:`QSqlQueryModel` class, which is the logical choice for
.. literalinclude:: sqlDialog.py
:linenos:
- :lines: 47-59
-
-In ``setRecipient()``, you set a filter over the returned results from the database, and
-emit a signal every time the recipient of the message changes.
-
-.. literalinclude:: sqlDialog.py
- :linenos:
- :lines: 61-70
+ :lines: 48-60
The ``data()`` function falls back to ``QSqlTableModel``'s implementation if the role is not a
custom user role.
.. literalinclude:: sqlDialog.py
:linenos:
- :lines: 72-79
+ :lines: 62-69
In ``roleNames()``, we return a Python dictionary with our custom role and role names as key-values
pairs, so we can use these roles in QML.
Alternatively, it can be useful to declare an Enum to hold all of the role values.
-Note that ``names`` has to be a hash to be used as a dictionary key,
-and that's why we're using the ``hash`` function.
.. literalinclude:: sqlDialog.py
:linenos:
- :lines: 81-95
+ :lines: 71-78
The ``send_message()`` function uses the given recipient and message to insert a new record into
the database.
.. literalinclude:: sqlDialog.py
:linenos:
- :lines: 97-116
+ :lines: 80-99
-chat.qml
+Main.qml
--------
-Let's look at the ``chat.qml`` file.
+Let's look at the ``Main.qml`` file.
-.. literalinclude:: chat.qml
+.. literalinclude:: App/Main.qml
:linenos:
:lines: 4-6
Among other things, this provides access to ``ApplicationWindow``, which replaces the existing
root type, Window:
-Let's step through the ``chat.qml`` file.
+Let's step through the ``App/Main.qml`` file.
-.. literalinclude:: chat.qml
+.. literalinclude:: App/Main.qml
:linenos:
:lines: 9-14
Because we are exposing the :code:`SqlConversationModel` class to QML, we will
declare a component to access it:
-.. literalinclude:: chat.qml
+.. literalinclude:: App/Main.qml
:linenos:
:lines: 16-18
resizable user interfaces.
Below, we use `ColumnLayout`_ to vertically lay out a `ListView`_ and a `Pane`_.
- .. literalinclude:: chat.qml
+ .. literalinclude:: App/Main.qml
:linenos:
:lines: 20-23
- .. literalinclude:: chat.qml
+ .. literalinclude:: App/Main.qml
:linenos:
:lines: 72-74
Let's look at the ``Listview`` in detail:
-.. literalinclude:: chat.qml
+.. literalinclude:: App/Main.qml
:linenos:
:lines: 23-70
button to send the message.
We use Pane to cover the area under these two items:
-.. literalinclude:: chat.qml
+.. literalinclude:: App/Main.qml
:linenos:
:lines: 72-96
.. _displayMarginEnd: https://doc.qt.io/qt-5/qml-qtquick-listview.html#displayMarginEnd-prop
.. _TextArea: https://doc.qt.io/qt-5/qml-qtquick-controls2-textarea.html
+``Main.qml`` needs to be put into a directory named :code:`App` along
+with a file named ``qmldir`` to describe a basic QML module:
+
+.. literalinclude:: App/qmldir
main.py
-------
.. literalinclude:: main.py
:linenos:
- :lines: 4-16
+ :lines: 5-17
``connectToDatabase()`` creates a connection with the SQLite database, creating the actual file
if it doesn't already exist.
.. literalinclude:: main.py
:linenos:
- :lines: 19-39
+ :lines: 20-41
A few interesting things happen in the ``main`` function:
.. literalinclude:: main.py
:linenos:
- :lines: 42-52
+ :lines: 45-59
.. image:: example_list_view.png
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
import datetime
import logging
self.select()
logging.debug("Table was loaded successfully.")
- def setRecipient(self, recipient):
- if recipient == self.recipient:
- pass
-
- self.recipient = recipient
-
- filter_str = (f"(recipient = '{self.recipient}' AND author = 'Me') OR "
- f"(recipient = 'Me' AND author='{self.recipient}')")
- self.setFilter(filter_str)
- self.select()
-
def data(self, index, role):
if role < Qt.UserRole:
return QSqlTableModel.data(self, index, role)
def roleNames(self):
"""Converts dict to hash because that's the result expected
by QSqlTableModel"""
- names = {}
- author = "author".encode()
- recipient = "recipient".encode()
- timestamp = "timestamp".encode()
- message = "message".encode()
-
- names[hash(Qt.UserRole)] = author
- names[hash(Qt.UserRole + 1)] = recipient
- names[hash(Qt.UserRole + 2)] = timestamp
- names[hash(Qt.UserRole + 3)] = message
-
- return names
+
+ return {int(Qt.UserRole): b"author",
+ Qt.UserRole + 1: b"recipient",
+ Qt.UserRole + 2: b"timestamp",
+ Qt.UserRole + 3: b"message"}
# This is a workaround because PySide doesn't provide Q_INVOKABLE
# So we declare this as a Slot to be able to call it from QML
set(libpyside_HEADERS # installed below
class_property.h
dynamicqmetaobject.h
+ dynamicslot_p.h
feature_select.h
- globalreceiverv2.h
pysideclassdecorator_p.h
pysideclassinfo.h
pysideclassinfo_p.h
set(libpyside_SRC
class_property.cpp
dynamicqmetaobject.cpp
+ dynamicslot.cpp
feature_select.cpp
signalmanager.cpp
- globalreceiverv2.cpp
pysideclassdecorator.cpp
pysideclassinfo.cpp
pysideqenum.cpp
// We call `__init__` while pretending to be a PyProperty_Type instance.
static int PyClassProperty_tp_init(PyObject *self, PyObject *args, PyObject *kwargs)
{
- auto hold = Py_TYPE(self);
+ auto *hold = Py_TYPE(self);
self->ob_type = &PyProperty_Type;
auto ret = PepExt_Type_GetInitSlot(&PyProperty_Type)(self, args, kwargs);
self->ob_type = hold;
{
// Use `_PepType_Lookup()` instead of `PyObject_GetAttr()` in order to get the raw
// descriptor (`property`) instead of calling `tp_descr_get` (`property.__get__()`).
- auto type = reinterpret_cast<PyTypeObject *>(obj);
+ auto *type = reinterpret_cast<PyTypeObject *>(obj);
PySide::Feature::Select(type);
PyObject *descr = _PepType_Lookup(type, name);
// 1. `Type.class_prop = value` --> descr_set: `Type.class_prop.__set__(value)`
// 2. `Type.class_prop = other_class_prop` --> setattro: replace existing `class_prop`
// 3. `Type.regular_attribute = value` --> setattro: regular attribute assignment
- const auto class_prop = reinterpret_cast<PyObject *>(PyClassProperty_TypeF());
+ auto *class_prop = reinterpret_cast<PyObject *>(PyClassProperty_TypeF());
const auto call_descr_set = descr && PyObject_IsInstance(descr, class_prop)
&& !PyObject_IsInstance(value, class_prop);
if (call_descr_set) {
return;
Py_INCREF(PyClassProperty_TypeF());
- auto classproptype = reinterpret_cast<PyObject *>(PyClassProperty_TypeF());
+ auto *classproptype = reinterpret_cast<PyObject *>(PyClassProperty_TypeF());
PyModule_AddObject(module, "PyClassProperty", classproptype);
}
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "dynamicqmetaobject.h"
+#include "pysidelogging_p.h"
#include "pysideqobject.h"
#include "pysidesignal.h"
#include "pysidesignal_p.h"
MetaObjectBuilder::~MetaObjectBuilder()
{
- for (auto *metaObject : m_d->m_cachedMetaObjects)
+ for (const auto *metaObject : m_d->m_cachedMetaObjects)
free(const_cast<QMetaObject*>(metaObject));
delete m_d->m_builder;
delete m_d;
+ ensureBuilder()->addSlot(signature).index();
}
-int MetaObjectBuilder::addSlot(const char *signature)
+int MetaObjectBuilder::addSlot(const QByteArray &signature)
{
return m_d->addSlot(signature);
}
return m_baseObject->methodCount() + methodBuilder.index();
}
-int MetaObjectBuilder::addSlot(const char *signature, const char *type)
+int MetaObjectBuilder::addSlot(const QByteArray &signature, const QByteArray &type)
{
return m_d->addSlot(signature, type);
}
+ ensureBuilder()->addSignal(signature).index();
}
-int MetaObjectBuilder::addSignal(const char *signature)
+int MetaObjectBuilder::addSignal(const QByteArray &signature)
{
return m_d->addSignal(signature);
}
int index)
{
index -= m_baseObject->methodCount();
- auto builder = ensureBuilder();
+ auto *builder = ensureBuilder();
Q_ASSERT(index >= 0 && index < builder->methodCount());
switch (mtype) {
case QMetaMethod::Constructor:
return notifyId;
}
+static QByteArray msgInvalidPropertyType(const QByteArray &className,
+ const QByteArray &propertyName,
+ const QByteArray &propertyType)
+{
+ return "QMetaObjectBuilder: Failed to add property \""_ba + propertyName
+ + "\" to \""_ba + className + "\": Invalid property type \""
+ + propertyType + "\"."_ba;
+}
+
QMetaPropertyBuilder
MetaObjectBuilderPrivate::createProperty(PySideProperty *property,
const QByteArray &propertyName)
}
}
}
- return builder->addProperty(propertyName, property->d->typeName,
- propertyNotifyId);
+ const auto metaType = QMetaType::fromName(property->d->typeName);
+ if (!metaType.isValid()) {
+ const auto &msg = msgInvalidPropertyType(m_builder->className(), propertyName,
+ property->d->typeName);
+ PyErr_WarnEx(PyExc_RuntimeWarning, msg.constData(), 0);
+ }
+ return builder->addProperty(propertyName, property->d->typeName, metaType, propertyNotifyId);
}
int MetaObjectBuilderPrivate::addProperty(const QByteArray &propertyName,
void MetaObjectBuilderPrivate::addInfo(const QMap<QByteArray, QByteArray> &info)
{
- auto builder = ensureBuilder();
+ auto *builder = ensureBuilder();
for (auto i = info.constBegin(), end = info.constEnd(); i != end; ++i)
builder->addClassInfo(i.key(), i.value());
m_dirty = true;
void MetaObjectBuilderPrivate::addEnumerator(const char *name, bool flag, bool scoped,
const MetaObjectBuilder::EnumValues &entries)
{
- auto builder = ensureBuilder();
+ auto *builder = ensureBuilder();
int have_already = builder->indexOfEnumerator(name);
if (have_already >= 0)
builder->removeEnumerator(have_already);
void MetaObjectBuilderPrivate::removeProperty(int index)
{
index -= m_baseObject->propertyCount();
- auto builder = ensureBuilder();
+ auto *builder = ensureBuilder();
Q_ASSERT(index >= 0 && index < builder->propertyCount());
builder->removeProperty(index);
m_dirty = true;
QTextStream str(&result);
str << "PySide" << QT_VERSION_MAJOR << ".QtCore.QMetaObject(\""
<< metaObject->className() << '"';
- if (auto *s = metaObject->superClass())
+ if (const auto *s = metaObject->superClass())
str << " inherits \"" << s->className() << '"';
str << ":\n";
// This enforces registering of all signals and slots at type parsing time, and not later at
// signal connection time, thus making sure no method indices change which would break
// existing connections.
- const PyObject *mro = type->tp_mro;
- const Py_ssize_t basesCount = PyTuple_GET_SIZE(mro);
+ PyObject *mro = type->tp_mro;
+ const Py_ssize_t basesCount = PyTuple_Size(mro);
std::vector<PyTypeObject *> basesToCheck;
// Prepend the actual type that we are parsing.
- basesToCheck.reserve(1u + basesCount);
+ basesToCheck.reserve(1U + basesCount);
basesToCheck.push_back(type);
- auto sbkObjTypeF = SbkObject_TypeF();
- auto baseObjType = reinterpret_cast<PyTypeObject *>(&PyBaseObject_Type);
+ auto *sbkObjTypeF = SbkObject_TypeF();
+ auto *baseObjType = reinterpret_cast<PyTypeObject *>(&PyBaseObject_Type);
for (Py_ssize_t i = 0; i < basesCount; ++i) {
- auto baseType = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, i));
+ auto *baseType = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, i));
if (baseType != sbkObjTypeF && baseType != baseObjType
&& !PySide::isQObjectDerived(baseType, false)) {
basesToCheck.push_back(baseType);
AutoDecRef obName(PyObject_GetAttr(obEnumType, PyMagicName::name()));
// Everything has been checked already in resolveDelayedQEnums.
// Therefore, we don't need to error-check here again.
- auto name = String::toCString(obName);
+ const auto *name = String::toCString(obName);
AutoDecRef members(PyObject_GetAttr(obEnumType, PyMagicName::members()));
AutoDecRef items(PyMapping_Items(members));
Py_ssize_t nr_items = PySequence_Length(items);
AutoDecRef key(PySequence_GetItem(item, 0));
AutoDecRef member(PySequence_GetItem(item, 1));
AutoDecRef value(PyObject_GetAttr(member, Shiboken::PyName::value()));
- auto ckey = String::toCString(key);
+ const auto *ckey = String::toCString(key);
auto ivalue = PyLong_AsSsize_t(value);
entries.push_back(std::make_pair(ckey, int(ivalue)));
}
int indexOfMethod(QMetaMethod::MethodType mtype, const QByteArray &signature) const;
int indexOfProperty(const QByteArray &name) const;
- int addSlot(const char *signature);
- int addSlot(const char *signature, const char *type);
- int addSignal(const char *signature);
+ int addSlot(const QByteArray &signature);
+ int addSlot(const QByteArray &signature, const QByteArray &type);
+ int addSignal(const QByteArray &signature);
void removeMethod(QMetaMethod::MethodType mtype, int index);
int addProperty(const char *property, PyObject *data);
void addInfo(const char *key, const char *value);
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "dynamicslot_p.h"
+#include "pysidestaticstrings.h"
+#include "pysideutils.h"
+#include "pysideweakref.h"
+#include "signalmanager.h"
+
+#include <autodecref.h>
+#include <gilstate.h>
+#include <pep384ext.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QtCompare>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QHash>
+#include <QtCore/QPointer>
+
+namespace PySide
+{
+
+static void disconnectReceiver(PyObject *pythonSelf);
+
+DynamicSlot::SlotType DynamicSlot::slotType(PyObject *callback)
+{
+ if (PyMethod_Check(callback) != 0)
+ return SlotType::Method;
+ if (PySide::isCompiledMethod(callback) != 0)
+ return SlotType::CompiledMethod;
+ if (PyCFunction_Check(callback) != 0)
+ return SlotType::C_Function;
+ return SlotType::Callable;
+}
+
+// Simple callable slot.
+class CallbackDynamicSlot : public DynamicSlot
+{
+ Q_DISABLE_COPY_MOVE(CallbackDynamicSlot)
+public:
+ explicit CallbackDynamicSlot(PyObject *callback) noexcept;
+ ~CallbackDynamicSlot() override;
+
+ void call(const QByteArrayList ¶meterTypes, const char *returnType,
+ void **cppArgs) override;
+ void formatDebug(QDebug &debug) const override;
+
+private:
+ PyObject *m_callback;
+};
+
+CallbackDynamicSlot::CallbackDynamicSlot(PyObject *callback) noexcept :
+ m_callback(callback)
+{
+ Py_INCREF(m_callback);
+}
+
+CallbackDynamicSlot::~CallbackDynamicSlot()
+{
+ Shiboken::GilState gil;
+ Py_DECREF(m_callback);
+}
+
+void CallbackDynamicSlot::call(const QByteArrayList ¶meterTypes, const char *returnType,
+ void **cppArgs)
+{
+ SignalManager::callPythonMetaMethod(parameterTypes, returnType, cppArgs, m_callback);
+ // SignalManager::callPythonMetaMethod might have failed, in that case we have to print the
+ // error so it considered "handled".
+ if (PyErr_Occurred() != nullptr)
+ SignalManager::handleMetaCallError();
+}
+
+void CallbackDynamicSlot::formatDebug(QDebug &debug) const
+{
+ debug << "CallbackDynamicSlot(" << PySide::debugPyObject(m_callback) << ')';
+}
+
+// A method given by "signal.connect(foo.method)" is a temporarily created
+// callable/partial function where self is bound as a first parameter.
+// It can be split into self and the function. Keeping a reference on
+// the callable itself would prevent object deletion. Instead, keep a
+// reference on the function.
+class MethodDynamicSlot : public DynamicSlot
+{
+ Q_DISABLE_COPY_MOVE(MethodDynamicSlot)
+public:
+
+ explicit MethodDynamicSlot(PyObject *function, PyObject *pythonSelf);
+ ~MethodDynamicSlot() override;
+
+ PyObject *pythonSelf() const { return m_pythonSelf; }
+
+ void call(const QByteArrayList ¶meterTypes, const char *returnType,
+ void **cppArgs) override;
+ void formatDebug(QDebug &debug) const override;
+
+private:
+ PyObject *m_function;
+ PyObject *m_pythonSelf;
+};
+
+MethodDynamicSlot::MethodDynamicSlot(PyObject *function, PyObject *pythonSelf) :
+ m_function(function),
+ m_pythonSelf(pythonSelf)
+{
+}
+
+MethodDynamicSlot::~MethodDynamicSlot()
+{
+ Shiboken::GilState gil;
+ Py_DECREF(m_function);
+}
+
+void MethodDynamicSlot::call(const QByteArrayList ¶meterTypes, const char *returnType,
+ void **cppArgs)
+{
+ // create a callback based on method data
+ Shiboken::AutoDecRef callable(PepExt_Type_CallDescrGet(m_function,
+ m_pythonSelf, nullptr));
+ SignalManager::callPythonMetaMethod(parameterTypes, returnType,
+ cppArgs, callable.object());
+ // SignalManager::callPythonMetaMethod might have failed, in that case we have to print the
+ // error so it considered "handled".
+ if (PyErr_Occurred() != nullptr)
+ SignalManager::handleMetaCallError();
+}
+
+void MethodDynamicSlot::formatDebug(QDebug &debug) const
+{
+ debug << "MethodDynamicSlot(self=" << PySide::debugPyObject(m_pythonSelf)
+ << ", function=" << PySide::debugPyObject(m_function) << ')';
+}
+
+// Store a weak reference on pythonSelf.
+class TrackingMethodDynamicSlot : public MethodDynamicSlot
+{
+ Q_DISABLE_COPY_MOVE(TrackingMethodDynamicSlot)
+public:
+ explicit TrackingMethodDynamicSlot(PyObject *function, PyObject *pythonSelf,
+ PyObject *weakRef);
+ ~TrackingMethodDynamicSlot() override;
+
+ void releaseWeakRef() { m_weakRef = nullptr; }
+
+private:
+ PyObject *m_weakRef;
+};
+
+TrackingMethodDynamicSlot::TrackingMethodDynamicSlot(PyObject *function, PyObject *pythonSelf,
+ PyObject *weakRef) :
+ MethodDynamicSlot(function, pythonSelf),
+ m_weakRef(weakRef)
+{
+}
+
+TrackingMethodDynamicSlot::~TrackingMethodDynamicSlot()
+{
+ if (m_weakRef != nullptr) {
+ Shiboken::GilState gil;
+ // weakrefs must not be de-refed after the object has been deleted,
+ // else they get negative refcounts.
+ if (PepExt_Weakref_IsAlive(m_weakRef))
+ Py_DECREF(m_weakRef);
+ }
+}
+
+// Delete the connection on receiver deletion by weakref
+class PysideReceiverMethodSlot : public TrackingMethodDynamicSlot
+{
+ Q_DISABLE_COPY_MOVE(PysideReceiverMethodSlot)
+public:
+ explicit PysideReceiverMethodSlot(PyObject *function, PyObject *pythonSelf);
+
+ ~PysideReceiverMethodSlot() override = default;
+};
+
+static void onPysideReceiverSlotDestroyed(void *data)
+{
+ auto *self = reinterpret_cast<PysideReceiverMethodSlot *>(data);
+ // Ensure the weakref is gone in case the connection stored in
+ // Qt's internals outlives Python.
+ self->releaseWeakRef();
+ Py_BEGIN_ALLOW_THREADS
+ disconnectReceiver(self->pythonSelf());
+ Py_END_ALLOW_THREADS
+}
+
+PysideReceiverMethodSlot::PysideReceiverMethodSlot(PyObject *function, PyObject *pythonSelf) :
+ TrackingMethodDynamicSlot(function, pythonSelf,
+ WeakRef::create(pythonSelf, onPysideReceiverSlotDestroyed, this))
+{
+}
+
+DynamicSlot* DynamicSlot::create(PyObject *callback)
+{
+ Shiboken::GilState gil;
+ switch (slotType(callback)) {
+ case SlotType::Method: {
+ PyObject *function = PyMethod_GET_FUNCTION(callback);
+ Py_INCREF(function);
+ PyObject *pythonSelf = PyMethod_GET_SELF(callback);
+ return new PysideReceiverMethodSlot(function, pythonSelf);
+ }
+ case SlotType::CompiledMethod: {
+ // PYSIDE-1523: PyMethod_Check is not accepting compiled form, we just go by attributes.
+ PyObject *function = PyObject_GetAttr(callback, PySide::PySideName::im_func());
+ Py_DECREF(function);
+ PyObject *pythonSelf = PyObject_GetAttr(callback, PySide::PySideName::im_self());
+ Py_DECREF(pythonSelf);
+ return new PysideReceiverMethodSlot(function, pythonSelf);
+ }
+ case SlotType::C_Function: // Treat C-function as normal callables
+ case SlotType::Callable:
+ break;
+ }
+ return new CallbackDynamicSlot(callback);
+}
+
+QDebug operator<<(QDebug debug, const DynamicSlot *ds)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ if (ds != nullptr)
+ ds->formatDebug(debug);
+ else
+ debug << "DynamicSlot(0";
+ return debug;
+}
+
+// Connection code for signal connections that use a Python callable not
+// targeting the QMetaMethod of a QObject (no index). They require
+// invocation in a class inheriting QtPrivate::QSlotObjectBase (PySideQSlotObject
+// aggregating DynamicSlot), which is passed to:
+// QObjectPrivate::connect(const QObject *, int signal, QtPrivate::QSlotObjectBase *, ...).
+// For each of those connections (identified by ConnectionKey), we maintain a
+// hash of ConnectionKey->QMetaObject::Connection for:
+//
+// - Disconnecting: Retrieve QMetaObject::Connection for the connection parameters
+//
+// - Tracking sender (QObject) life time and clean hash on destroyed()
+//
+// - Tracking receiver (PyObject*) life time via weakref in case of a
+// connection to a method and proactively disconnect on weakref
+// notification as not to cause leaks of the instance.
+
+struct ConnectionKey
+{
+ const QObject *sender;
+ int senderIndex;
+ const PyObject *object;
+ const void *method;
+
+ friend constexpr size_t qHash(const ConnectionKey &k, size_t seed = 0) noexcept
+ {
+ return qHashMulti(seed, k.sender, k.senderIndex, k.object, k.method);
+ }
+
+ friend constexpr bool comparesEqual(const ConnectionKey &lhs,
+ const ConnectionKey &rhs) noexcept
+ {
+ return lhs.sender == rhs.sender && lhs.senderIndex == rhs.senderIndex
+ && lhs.object == rhs.object && lhs.method == rhs.method;
+ }
+
+ Q_DECLARE_EQUALITY_COMPARABLE(ConnectionKey);
+};
+
+QDebug operator<<(QDebug debug, const ConnectionKey &k)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "ConnectionKey(" << static_cast<const void *>(k.sender)
+ << '/' << k.sender->metaObject()->className();
+ auto on = k.sender->objectName();
+ if (!on.isEmpty())
+ debug << "/\"" << on << '"';
+ debug << ", index=" << k.senderIndex << ", target="
+ << PySide::debugPyObject(const_cast<PyObject *>(k.object));
+ if (k.method != nullptr)
+ debug << ", method=" << k.method;
+ debug << ')';
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const QMetaObject::Connection &c)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "Connection(";
+ if (c)
+ debug << static_cast<const void *>(&c); // d-ptr;
+ else
+ debug << '0';
+ debug << ')';
+ return debug;
+}
+
+using ConnectionHash = QHash<ConnectionKey, QMetaObject::Connection>;
+
+static ConnectionHash connectionHash;
+
+static ConnectionKey connectionKey(const QObject *sender, int senderIndex,
+ PyObject *callback)
+{
+ PyObject *object{};
+ void *method{};
+
+ switch (DynamicSlot::slotType(callback)) {
+ case DynamicSlot::SlotType::Method:
+ // PYSIDE-1422: Avoid hash on self which might be unhashable.
+ object = PyMethod_GET_SELF(callback);
+ method = PyMethod_GET_FUNCTION(callback);
+ break;
+ case DynamicSlot::SlotType::CompiledMethod: {
+ // PYSIDE-1589: Fix for slots in compiled functions
+ Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PySideName::im_self()));
+ Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PySideName::im_func()));
+ object = self.object();
+ method = func.object();
+ break;
+ }
+ case DynamicSlot::SlotType::Callable:
+ method = callback;
+ break;
+ case DynamicSlot::SlotType::C_Function:
+ object = PyCFunction_GetSelf(callback);
+ method = reinterpret_cast<void *>(PyCFunction_GetFunction(callback));
+ break;
+ }
+
+ return {sender, senderIndex, object, method};
+}
+
+// Listens to QObject::destroyed of senders and removes them from the hash.
+class SenderSignalDeletionTracker : public QObject
+{
+ Q_OBJECT
+public:
+ using QObject::QObject;
+
+public Q_SLOTS:
+ void senderDestroyed(QObject *o);
+};
+
+void SenderSignalDeletionTracker::senderDestroyed(QObject *o)
+{
+ for (auto it = connectionHash.begin(); it != connectionHash.end(); ) {
+ if (it.key().sender == o)
+ it = connectionHash.erase(it);
+ else
+ ++it;
+ }
+}
+
+static QPointer<SenderSignalDeletionTracker> senderSignalDeletionTracker;
+
+static void disconnectReceiver(PyObject *pythonSelf)
+{
+ // A check for reentrancy was added for PYSIDE-88, but has not been
+ // observed yet.
+ for (bool keepGoing = true; keepGoing; ) {
+ keepGoing = false;
+ for (auto it = connectionHash.begin(); it != connectionHash.end(); ) {
+ if (it.key().object == pythonSelf) {
+ const auto oldSize = connectionHash.size();
+ QObject::disconnect(it.value());
+ it = connectionHash.erase(it);
+ // Check for a disconnection causing deletion of further objects
+ // by a re-entrant call.
+ if (connectionHash.size() < oldSize - 1) {
+ keepGoing = true;
+ break; // Iterators were invalidated, retry
+ }
+ } else {
+ ++it;
+ }
+ }
+ }
+}
+
+static void clearConnectionHash()
+{
+ connectionHash.clear();
+}
+
+void registerSlotConnection(QObject *source, int signalIndex, PyObject *callback,
+ const QMetaObject::Connection &connection)
+{
+ connectionHash.insert(connectionKey(source, signalIndex, callback), connection);
+ if (senderSignalDeletionTracker.isNull()) {
+ auto *app = QCoreApplication::instance();
+ senderSignalDeletionTracker = new SenderSignalDeletionTracker(app);
+ Py_AtExit(clearConnectionHash);
+ }
+
+ QObject::connect(source, &QObject::destroyed,
+ senderSignalDeletionTracker, &SenderSignalDeletionTracker::senderDestroyed,
+ Qt::UniqueConnection);
+}
+
+bool disconnectSlot(QObject *source, int signalIndex, PyObject *callback)
+{
+ auto it = connectionHash.find(connectionKey(source, signalIndex, callback));
+ const bool ok = it != connectionHash.end();
+ if (ok) {
+ QObject::disconnect(it.value());
+ connectionHash.erase(it);
+ }
+ return ok;
+}
+
+} // namespace PySide
+
+#include "dynamicslot.moc"
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef DYNAMICSLOT_P_H
+#define DYNAMICSLOT_P_H
+
+#include <sbkpython.h>
+
+#include <QtCore/QtCompare>
+#include <QtCore/QMetaObject>
+
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+namespace PySide
+{
+
+class DynamicSlot
+{
+ Q_DISABLE_COPY_MOVE(DynamicSlot)
+public:
+ enum SlotType
+ {
+ Callable,
+ Method,
+ CompiledMethod,
+ C_Function
+ };
+
+ virtual ~DynamicSlot() = default;
+
+ virtual void call(const QByteArrayList ¶meterTypes, const char *returnType,
+ void **cppArgs) = 0;
+ virtual void formatDebug(QDebug &debug) const = 0;
+
+ static SlotType slotType(PyObject *callback);
+ static DynamicSlot *create(PyObject *callback);
+
+protected:
+ DynamicSlot() noexcept = default;
+};
+
+QDebug operator<<(QDebug debug, const DynamicSlot *ds);
+
+void registerSlotConnection(QObject *source, int signalIndex, PyObject *callback,
+ const QMetaObject::Connection &connection);
+bool disconnectSlot(QObject *source, int signalIndex, PyObject *callback);
+
+}
+
+#endif // DYNAMICSLOT_P_H
return false;
setSelectId(new_dict, select_id);
// insert the dict into the ring
- auto next_dict = nextInCircle(dict);
+ auto *next_dict = nextInCircle(dict);
setNextDict(dict, new_dict);
setNextDict(new_dict, next_dict);
PepType_SetDict(type, new_dict);
last_select_id = select_id;
auto *mro = type->tp_mro;
- Py_ssize_t idx, n = PyTuple_GET_SIZE(mro);
+ const Py_ssize_t n = PyTuple_Size(mro);
// We leave 'Shiboken.Object' and 'object' alone, therefore "n - 2".
- for (idx = 0; idx < n - 2; idx++) {
- auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
+ for (Py_ssize_t idx = 0; idx < n - 2; idx++) {
+ auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
SelectFeatureSetSubtype(sub_type, select_id);
}
// PYSIDE-1436: Clear all caches for the type and subtypes.
* Create a method with a lower case name.
*/
auto *obtype = reinterpret_cast<PyObject *>(type);
- int len = strlen(new_name);
- auto name = new char[len + 1];
+ const auto len = strlen(new_name);
+ auto *name = new char[len + 1];
strcpy(name, new_name);
- auto new_meth = new PyMethodDef;
+ auto *new_meth = new PyMethodDef;
new_meth->ml_name = name;
new_meth->ml_meth = meth->ml_meth;
new_meth->ml_flags = meth->ml_flags;
/*
* Add objects with lower names to `type->tp_dict` from 'prev_dict`.
*/
- PyObject *key, *value;
+ PyObject *key{};
+ PyObject *value{};
Py_ssize_t pos = 0;
// We first copy the things over which will not be changed:
return prop;
}
-static const QByteArrayList parseFields(const char *propStr, bool *stdWrite)
+static QByteArrayList parseFields(const char *propStr, bool *stdWrite)
{
/*
* Break the string into subfields at ':' and add defaults.
auto res = QByteArrayList();
PyObject *mro = type->tp_mro;
- const Py_ssize_t n = PyTuple_GET_SIZE(mro);
+ const Py_ssize_t n = PyTuple_Size(mro);
// We leave 'Shiboken.Object' and 'object' alone, therefore "n - 2".
for (Py_ssize_t idx = 0; idx < n - 2; idx++) {
- auto *subType = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
- auto props = SbkObjectType_GetPropertyStrings(subType);
+ auto *subType = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
+ auto *props = SbkObjectType_GetPropertyStrings(subType);
if (props != nullptr)
for (; *props != nullptr; ++props)
res << QByteArray(*props);
return true;
for (const auto &propStr : std::as_const(props)) {
- bool isStdWrite;
+ bool isStdWrite{};
auto fields = parseFields(propStr, &isStdWrite);
bool haveWrite = fields.size() == 3;
PyObject *name = make_snake_case(fields[0], lower);
static bool patch_property_impl()
{
// Turn `__doc__` into a computed attribute without changing writability.
- auto gsp = property_getset;
+ auto *gsp = property_getset;
auto *type = &PyProperty_Type;
AutoDecRef dict(PepType_GetDict(type));
AutoDecRef descr(PyDescr_NewGetSet(type, gsp));
+++ /dev/null
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "globalreceiverv2.h"
-#include "pysideweakref.h"
-#include "pysidestaticstrings.h"
-#include "pysideutils.h"
-#include "signalmanager.h"
-
-#include <autodecref.h>
-#include <gilstate.h>
-#include <pep384ext.h>
-
-#include <QtCore/QMetaMethod>
-#include <QtCore/QSet>
-#include <QtCore/QDebug>
-
-#include <cstring>
-
-#define RECEIVER_DESTROYED_SLOT_NAME "__receiverDestroyed__(QObject*)"
-
-
-namespace PySide
-{
-
-class DynamicSlotDataV2
-{
- Q_DISABLE_COPY_MOVE(DynamicSlotDataV2)
- public:
- DynamicSlotDataV2(PyObject *callback, GlobalReceiverV2 *parent);
- ~DynamicSlotDataV2();
-
- int addSlot(const char *signature);
- int id(const char *signature) const;
- PyObject *callback();
- GlobalReceiverKey key() const { return {m_pythonSelf, m_callback}; }
- void notify();
-
- static void onCallbackDestroyed(void *data);
- static GlobalReceiverKey key(PyObject *callback);
-
- void formatDebug(QDebug &debug) const;
-
- private:
- bool m_isMethod;
- PyObject *m_callback;
- PyObject *m_pythonSelf = nullptr;
- PyObject *m_pyClass = nullptr;
- PyObject *m_weakRef = nullptr;
- QMap<QByteArray, int> m_signatures;
- GlobalReceiverV2 *m_parent;
-};
-
-void DynamicSlotDataV2::formatDebug(QDebug &debug) const
-{
- debug << "method=" << m_isMethod << ", m_callback=" << m_callback;
- if (m_callback != nullptr)
- debug << '/' << Py_TYPE(m_callback)->tp_name;
- debug << ", self=" << m_pythonSelf;
- if (m_pythonSelf != nullptr)
- debug << '/' << Py_TYPE(m_pythonSelf)->tp_name;
- debug << ", m_pyClass=" << m_pyClass;
- if (m_pyClass != nullptr)
- debug << '/' << Py_TYPE(m_pyClass)->tp_name;
- debug << ", signatures=" << m_signatures.keys();
-}
-
-QDebug operator<<(QDebug debug, const DynamicSlotDataV2 *d)
-{
- QDebugStateSaver saver(debug);
- debug.noquote();
- debug.nospace();
- debug << "DynamicSlotDataV2(";
- if (d)
- d->formatDebug(debug);
- else
- debug << '0';
- debug << ')';
- return debug;
-}
-
-using namespace PySide;
-
-DynamicSlotDataV2::DynamicSlotDataV2(PyObject *callback, GlobalReceiverV2 *parent) :
- m_parent(parent)
-{
- Shiboken::GilState gil;
-
- if (PyMethod_Check(callback)) {
- m_isMethod = true;
- // To avoid increment instance reference keep the callback information
- m_callback = PyMethod_GET_FUNCTION(callback);
- Py_INCREF(m_callback);
- m_pythonSelf = PyMethod_GET_SELF(callback);
-
- //monitor class from method lifetime
- m_weakRef = WeakRef::create(m_pythonSelf, DynamicSlotDataV2::onCallbackDestroyed, this);
- } else if (PySide::isCompiledMethod(callback)) {
- // PYSIDE-1523: PyMethod_Check is not accepting compiled form, we just go by attributes.
- m_isMethod = true;
-
- m_callback = PyObject_GetAttr(callback, PySide::PySideName::im_func());
- Py_DECREF(m_callback);
-
- m_pythonSelf = PyObject_GetAttr(callback, PySide::PySideName::im_self());
- Py_DECREF(m_pythonSelf);
-
- //monitor class from method lifetime
- m_weakRef = WeakRef::create(m_pythonSelf, DynamicSlotDataV2::onCallbackDestroyed, this);
- } else {
- m_isMethod = false;
-
- m_callback = callback;
- Py_INCREF(m_callback);
- }
-}
-
-GlobalReceiverKey DynamicSlotDataV2::key(PyObject *callback)
-{
- Shiboken::GilState gil;
- if (PyMethod_Check(callback)) {
- // PYSIDE-1422: Avoid hash on self which might be unhashable.
- return {PyMethod_GET_SELF(callback), PyMethod_GET_FUNCTION(callback)};
- } else if (PySide::isCompiledMethod(callback)) {
- // PYSIDE-1589: Fix for slots in compiled functions
- Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PySideName::im_self()));
- Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PySideName::im_func()));
- return {self, func};
- }
- return {nullptr, callback};
-}
-
-PyObject *DynamicSlotDataV2::callback()
-{
- PyObject *callback = m_callback;
-
- //create a callback based on method data
- if (m_isMethod)
- callback = PepExt_Type_CallDescrGet(m_callback, m_pythonSelf, nullptr);
- else
- Py_INCREF(callback);
-
- return callback;
-}
-
-int DynamicSlotDataV2::id(const char *signature) const
-{
- const auto it = m_signatures.constFind(signature);
- return it != m_signatures.cend() ? it.value() : -1;
-}
-
-int DynamicSlotDataV2::addSlot(const char *signature)
-{
- int index = id(signature);
- if (index == -1)
- index = m_signatures[signature] = m_parent->metaObjectBuilder().addSlot(signature);
- return index;
-}
-
-void DynamicSlotDataV2::onCallbackDestroyed(void *data)
-{
- auto self = reinterpret_cast<DynamicSlotDataV2 *>(data);
- self->m_weakRef = nullptr;
- Py_BEGIN_ALLOW_THREADS
- SignalManager::instance().deleteGlobalReceiver(self->m_parent);
- Py_END_ALLOW_THREADS
-}
-
-DynamicSlotDataV2::~DynamicSlotDataV2()
-{
- Shiboken::GilState gil;
-
- Py_XDECREF(m_weakRef);
- m_weakRef = nullptr;
-
- Py_DECREF(m_callback);
-}
-
-const char *GlobalReceiverV2::senderDynamicProperty = "_q_pyside_sender";
-
-GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, QObject *receiver) :
- QObject(nullptr),
- m_metaObject("__GlobalReceiver__", &QObject::staticMetaObject),
- m_receiver(receiver)
-{
- m_data = new DynamicSlotDataV2(callback, this);
-}
-
-GlobalReceiverV2::~GlobalReceiverV2()
-{
- m_refs.clear();
- // Remove itself from map.
- // Suppress handling of destroyed() for objects whose last reference is contained inside
- // the callback object that will now be deleted. The reference could be a default argument,
- // a callback local variable, etc.
- // The signal has to be suppressed because it would lead to the following situation:
- // Callback is deleted, hence the last reference is decremented,
- // leading to the object being deleted, which emits destroyed(), which would try to invoke
- // the already deleted callback, and also try to delete the object again.
- DynamicSlotDataV2 *data = m_data;
- m_data = nullptr;
- delete data;
-}
-
-int GlobalReceiverV2::addSlot(const char *signature)
-{
- return m_data->addSlot(signature);
-}
-
-void GlobalReceiverV2::incRef(const QObject *link)
-{
- Q_ASSERT(link);
- m_refs.append(link);
-}
-
-void GlobalReceiverV2::decRef(const QObject *link)
-{
- Q_ASSERT(link);
- m_refs.removeOne(link);
-}
-
-void GlobalReceiverV2::notify()
-{
- purgeDeletedSenders();
-}
-
-static bool isNull(const QPointer<const QObject> &p)
-{
- return p.isNull();
-}
-
-void GlobalReceiverV2::purgeDeletedSenders()
-{
- m_refs.erase(std::remove_if(m_refs.begin(), m_refs.end(), isNull), m_refs.end());
-}
-
-bool GlobalReceiverV2::isEmpty() const
-{
- return std::all_of(m_refs.cbegin(), m_refs.cend(), isNull);
-}
-
-GlobalReceiverKey GlobalReceiverV2::key() const
-{
- return m_data->key();
-}
-
-GlobalReceiverKey GlobalReceiverV2::key(PyObject *callback)
-{
- return DynamicSlotDataV2::key(callback);
-}
-
-const QMetaObject *GlobalReceiverV2::metaObject() const
-{
- return const_cast<GlobalReceiverV2 *>(this)->m_metaObject.update();
-}
-
-int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void **args)
-{
- Shiboken::GilState gil;
- Q_ASSERT(call == QMetaObject::InvokeMetaMethod);
- Q_ASSERT(id >= QObject::staticMetaObject.methodCount());
-
- QMetaMethod slot = metaObject()->method(id);
- Q_ASSERT(slot.methodType() == QMetaMethod::Slot);
-
- if (!m_data) {
- const QByteArray message = "PySide6 Warning: Skipping callback call "
- + slot.methodSignature() + " because the callback object is being destructed.";
- PyErr_WarnEx(PyExc_RuntimeWarning, message.constData(), 0);
- return -1;
- }
-
- const bool setSenderDynamicProperty = !m_receiver.isNull();
- if (setSenderDynamicProperty)
- m_receiver->setProperty(senderDynamicProperty, QVariant::fromValue(sender()));
-
- const bool isShortCuit = std::strchr(slot.methodSignature(), '(') == nullptr;
- Shiboken::AutoDecRef callback(m_data->callback());
- SignalManager::callPythonMetaMethod(slot, args, callback, isShortCuit);
-
- if (setSenderDynamicProperty)
- m_receiver->setProperty(senderDynamicProperty, QVariant{});
-
- // SignalManager::callPythonMetaMethod might have failed, in that case we have to print the
- // error so it considered "handled".
- if (PyErr_Occurred()) {
- int reclimit = Py_GetRecursionLimit();
- // Inspired by Python's errors.c: PyErr_GivenExceptionMatches() function.
- // Temporarily bump the recursion limit, so that PyErr_Print will not raise a recursion
- // error again. Don't do it when the limit is already insanely high, to avoid overflow.
- if (reclimit < (1 << 30))
- Py_SetRecursionLimit(reclimit + 5);
- PyErr_Print();
- Py_SetRecursionLimit(reclimit);
- }
-
- return -1;
-}
-
-void GlobalReceiverV2::formatDebug(QDebug &debug) const
-{
- debug << "receiver=" << m_receiver << ", slot=" << m_data;
- if (isEmpty())
- debug << ", empty";
- else
- debug << ", refs=" << m_refs;
-};
-
-QDebug operator<<(QDebug debug, const GlobalReceiverV2 *g)
-{
- QDebugStateSaver saver(debug);
- debug.noquote();
- debug.nospace();
- debug << "GlobalReceiverV2(";
- if (g)
- g->formatDebug(debug);
- else
- debug << '0';
- debug << ')';
- return debug;
-}
-
-} // namespace PySide
+++ /dev/null
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef GLOBALRECEIVER_V2_H
-#define GLOBALRECEIVER_V2_H
-
-#include <sbkpython.h>
-
-#include "dynamicqmetaobject.h"
-
-#include <QtCore/QtCompare>
-#include <QtCore/QByteArray>
-#include <QtCore/QHashFunctions>
-#include <QtCore/QObject>
-#include <QtCore/QPointer>
-#include <QtCore/QMap>
-
-#include <memory>
-
-QT_FORWARD_DECLARE_CLASS(QDebug);
-
-namespace PySide
-{
-
-class DynamicSlotDataV2;
-class GlobalReceiverV2;
-
-struct GlobalReceiverKey
-{
- const PyObject *object;
- const PyObject *method;
-
- friend constexpr size_t qHash(GlobalReceiverKey k, size_t seed = 0) noexcept
- {
- return qHashMulti(seed, k.object, k.method);
- }
- friend constexpr bool comparesEqual(const GlobalReceiverKey &lhs,
- const GlobalReceiverKey &rhs) noexcept
- {
- return lhs.object == rhs.object && lhs.method == rhs.method;
- }
- Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(GlobalReceiverKey)
-};
-
-/// A class used to link C++ Signals to non C++ slots (Python callbacks) by
-/// providing fake slots for QObject::connect().
-/// It keeps a Python callback and the list of QObject senders. It is stored
-/// in SignalManager by a hash of the Python callback.
-class GlobalReceiverV2 : public QObject
-{
-public:
- Q_DISABLE_COPY_MOVE(GlobalReceiverV2)
-
- /// Create a GlobalReceiver object that will call 'callback'
- /// @param callback A Python callable object (can be a method or not)
- explicit GlobalReceiverV2(PyObject *callback, QObject *receiver = nullptr);
-
- ~GlobalReceiverV2() override;
-
- /// Reimplemented function from QObject
- int qt_metacall(QMetaObject::Call call, int id, void **args) override;
- const QMetaObject *metaObject() const override;
-
- /// Add a extra slot to this object
- /// @param signature The signature of the slot to be added
- /// @return The index of this slot on metaobject
- int addSlot(const char *signature);
-
- /// Notify to GlobalReceiver about when a new connection was made
- void notify();
-
- /// Used to increment the reference of the GlobalReceiver object
- /// @param link This is a parameter used to link the ref to
- /// some QObject life.
- void incRef(const QObject *link);
-
- /// Used to decrement the reference of the GlobalReceiver object.
- /// @param link This is a parameter used to dismiss the link
- /// ref to some QObject.
- void decRef(const QObject *link);
-
- /// Returns whether any senders are registered.
- bool isEmpty() const;
-
- /// Use to retrieve the unique hash of this GlobalReceiver object
- /// @return hash key
- GlobalReceiverKey key() const;
-
- /// Use to retrieve the unique hash of the PyObject based on GlobalReceiver rules
- /// @param callback The Python callable object used to calculate the id
- /// @return hash key
- static GlobalReceiverKey key(PyObject *callback);
-
- const MetaObjectBuilder &metaObjectBuilder() const { return m_metaObject; }
- MetaObjectBuilder &metaObjectBuilder() { return m_metaObject; }
-
- static const char *senderDynamicProperty;
-
- void formatDebug(QDebug &debug) const;
-
-private:
- void purgeDeletedSenders();
-
- MetaObjectBuilder m_metaObject;
- DynamicSlotDataV2 *m_data;
- using QObjectPointer = QPointer<const QObject>;
- QList<QObjectPointer> m_refs;
- QPointer<QObject> m_receiver;
-};
-
-QDebug operator<<(QDebug debug, const GlobalReceiverV2 *g);
-
-}
-
-#endif
#include <optional>
#include <typeinfo>
+#ifdef Q_OS_WIN
+# include <conio.h>
+#else
+# include <QtCore/QDeadlineTimer>
+# include <QtCore/private/qcore_unix_p.h>
+#endif
+
using namespace Qt::StringLiterals;
static QStack<PySide::CleanupFunction> cleanupFunctionList;
ClassProperty::init(module);
MetaFunction::init(module);
// Init signal manager, so it will register some meta types used by QVariant.
- SignalManager::instance();
+ SignalManager::init();
initQApp();
}
if (origName.isEmpty())
return QByteArrayList{};
PyObject *mro = type->tp_mro;
- auto n = PyTuple_GET_SIZE(mro);
+ auto n = PyTuple_Size(mro);
auto len = std::strlen(origName);
for (Py_ssize_t idx = 0; idx < n; idx++) {
- PyTypeObject *base = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
+ auto *base = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
if (!SbkObjectType_Check(base))
continue;
- auto props = SbkObjectType_GetPropertyStrings(base);
+ auto *props = SbkObjectType_GetPropertyStrings(base);
if (props == nullptr || *props == nullptr)
continue;
for (; *props != nullptr; ++props) {
using Shiboken::AutoDecRef;
QByteArray propName(Shiboken::String::toCString(name));
- auto type = Py_TYPE(qObj);
+ auto *type = Py_TYPE(qObj);
int flags = currentSelectId(type);
int prop_flag = flags & 0x02;
auto found = false;
- QByteArray getterName{}, setterName{};
+ QByteArray getterName;
+ QByteArray setterName;
auto fields = _SbkType_LookupProperty(type, propName, flags);
if (!fields.isEmpty()) {
if (method.methodType() == QMetaMethod::Signal && method.name() == name)
return method;
}
- auto *base = mo->superClass();
+ const auto *base = mo->superClass();
return base != nullptr ? findSignal(base, name) : std::nullopt;
}
PyObject *kwds, bool allowErrors)
{
- PyObject *key, *value;
+ PyObject *key{};
+ PyObject *value{};
Py_ssize_t pos = 0;
int flags = currentSelectId(Py_TYPE(qObj));
int snake_flag = flags & 0x01;
static void destructionVisitor(SbkObject *pyObj, void *data)
{
- auto realData = reinterpret_cast<void **>(data);
- auto pyQApp = reinterpret_cast<SbkObject *>(realData[0]);
- auto pyQObjectType = reinterpret_cast<PyTypeObject *>(realData[1]);
+ auto *realData = reinterpret_cast<void **>(data);
+ auto *pyQApp = reinterpret_cast<SbkObject *>(realData[0]);
+ auto *pyQObjectType = reinterpret_cast<PyTypeObject *>(realData[1]);
if (pyObj != pyQApp && PyObject_TypeCheck(pyObj, pyQObjectType)) {
if (Shiboken::Object::hasOwnership(pyObj) && Shiboken::Object::isValid(pyObj, false)) {
QCoreApplication *app = QCoreApplication::instance();
if (!app)
return;
- SignalManager::instance().clear();
Shiboken::BindingManager &bm = Shiboken::BindingManager::instance();
SbkObject *pyQApp = bm.retrieveWrapper(app);
void initDynamicMetaObject(PyTypeObject *type, const QMetaObject *base, std::size_t cppObjSize)
{
//create DynamicMetaObject based on python type
- auto userData = new TypeUserData(reinterpret_cast<PyTypeObject *>(type), base, cppObjSize);
+ auto *userData = new TypeUserData(reinterpret_cast<PyTypeObject *>(type), base, cppObjSize);
userData->mo.update();
Shiboken::ObjectType::setTypeUserData(type, userData, Shiboken::callCppDestructor<TypeUserData>);
TypeUserData *retrieveTypeUserData(PyObject *pyObj)
{
- auto pyTypeObj = PyType_Check(pyObj)
+ auto *pyTypeObj = PyType_Check(pyObj)
? reinterpret_cast<PyTypeObject *>(pyObj) : Py_TYPE(pyObj);
return retrieveTypeUserData(pyTypeObj);
}
const QMetaObject *retrieveMetaObject(PyObject *pyObj)
{
- auto pyTypeObj = PyType_Check(pyObj)
+ auto *pyTypeObj = PyType_Check(pyObj)
? reinterpret_cast<PyTypeObject *>(pyObj) : Py_TYPE(pyObj);
return retrieveMetaObject(pyTypeObj);
}
{
PyTypeObject *qObjType = Shiboken::Conversions::getPythonTypeObject("QObject*");
- PyObject *bases = PyTuple_GET_ITEM(args, 1);
- int numBases = PyTuple_GET_SIZE(bases);
+ PyObject *bases = PyTuple_GetItem(args, 1);
TypeUserData *userData = nullptr;
- for (int i = 0; i < numBases; ++i) {
- auto base = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(bases, i));
+ for (Py_ssize_t i = 0, numBases = PyTuple_Size(bases); i < numBases; ++i) {
+ auto *base = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(bases, i));
if (PyType_IsSubtype(base, qObjType)) {
userData = retrieveTypeUserData(base);
break;
}
}
if (!userData) {
- const char *className = Shiboken::String::toCString(PyTuple_GET_ITEM(args, 0));
+ const char *className = Shiboken::String::toCString(PyTuple_GetItem(args, 0));
qWarning("Sub class of QObject not inheriting QObject!? Crash will happen when using %s.",
className);
return;
PySide::Feature::Enable(true);
}
+extern "C" {
+static int qAppInputHook()
+{
+ auto *app = QCoreApplication::instance();
+ if (app == nullptr || app->thread() != QThread::currentThread())
+ return 0;
+#ifndef Q_OS_WIN
+ // Check for press on stdin (file descriptor 0)
+ pollfd stdinPfd = qt_make_pollfd(0, POLLIN);
+ while (qt_safe_poll(&stdinPfd, 1, QDeadlineTimer{1}) == 0)
+ QCoreApplication::processEvents({}, 50000);
+#else
+ while (_kbhit() == 0)
+ QCoreApplication::processEvents({}, 50000);
+#endif
+ return 0;
+}
+} // extern "C"
+
+static void unregisterQAppInputHook()
+{
+ PyOS_InputHook = nullptr;
+}
+
+static void registerQAppInputHook()
+{
+ PyOS_InputHook = qAppInputHook;
+ qAddPostRoutine(unregisterQAppInputHook);
+}
+
void initQApp()
{
/*
* I would appreciate very much if someone could explain or even fix
* this issue. It exists only when a pre-existing application exists.
*/
- if (!qApp)
+ if (qApp == nullptr) {
+ registerQAppInputHook();
Py_DECREF(MakeQAppWrapper(nullptr));
+ }
// PYSIDE-1470: Register a function to destroy an application from shiboken.
setDestroyQApplication(destroyQCoreApplication);
// Search on metaobject (avoid internal attributes started with '__')
if (!attr) {
- PyObject *type, *value, *traceback;
+ PyObject *type{};
+ PyObject *value{};
+ PyObject *traceback{};
PyErr_Fetch(&type, &value, &traceback); // This was omitted for a loong time.
int flags = currentSelectId(Py_TYPE(self));
// Note: before implementing this property handling, the meta function code
// below created meta functions which was quite wrong.
auto *subdict = _PepType_Lookup(Py_TYPE(self), PySideMagicName::property_methods());
- PyObject *propName = PyDict_GetItem(subdict, name);
- if (propName) {
+ if (PyObject *propName = PyDict_GetItem(subdict, name)) {
// We really have a property name and need to fetch the fget or fset function.
static PyObject *const _fget = Shiboken::String::createStaticString("fget");
static PyObject *const _fset = Shiboken::String::createStaticString("fset");
static PyObject *const _fdel = Shiboken::String::createStaticString("fdel");
static PyObject *const arr[3] = {_fget, _fset, _fdel};
- auto prop = _PepType_Lookup(Py_TYPE(self), propName);
- for (int idx = 0; idx < 3; ++idx) {
- auto *trial = arr[idx];
+ auto *prop = _PepType_Lookup(Py_TYPE(self), propName);
+ for (auto *trial : arr) {
auto *res = PyObject_GetAttr(prop, trial);
if (res) {
AutoDecRef elemName(PyObject_GetAttr(res, PySideMagicName::name()));
if (methMatch) {
if (method.methodType() == QMetaMethod::Signal) {
signalList.append(method);
- } else {
- PySideMetaFunction *func = MetaFunction::newObject(cppSelf, i);
- if (func) {
- PyObject *result = reinterpret_cast<PyObject *>(func);
- PyObject_SetAttr(self, name, result);
- return result;
- }
+ } else if (auto *func = MetaFunction::newObject(cppSelf, i)) {
+ auto *result = reinterpret_cast<PyObject *>(func);
+ PyObject_SetAttr(self, name, result);
+ return result;
}
}
}
if (!signalList.isEmpty()) {
- PyObject *pySignal = reinterpret_cast<PyObject *>(
+ auto *pySignal = reinterpret_cast<PyObject *>(
Signal::newObjectFromMethod(self, signalList));
PyObject_SetAttr(self, name, pySignal);
return pySignal;
if (Py_IsInitialized() == 0)
return;
- Shiboken::GilState state;
-
- SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(object);
- if (wrapper != nullptr)
- Shiboken::BindingManager::instance().releaseWrapper(wrapper);
+ // Check for existence before locking (fix hang when QObjects
+ // are moved to different threads).
+ auto &bindingManager = Shiboken::BindingManager::instance();
+ if (bindingManager.hasWrapper(object)) {
+ Shiboken::GilState state;
+ if (SbkObject *wrapper = bindingManager.retrieveWrapper(object))
+ bindingManager.releaseWrapper(wrapper);
+ }
}
static const char invalidatePropertyName[] = "_PySideInvalidatePtr";
static const QMetaObject *metaObjectCandidate(const QObject *o)
{
- auto *metaObject = o->metaObject();
+ const auto *metaObject = o->metaObject();
// Skip QML helper types and Python objects
if (hasDynamicMetaObject(o)) {
- if (auto *super = metaObject->superClass())
+ if (const auto *super = metaObject->superClass())
metaObject = super;
}
- for (auto *candidate = metaObject; candidate != nullptr; candidate = candidate->superClass()) {
+ for (const auto *candidate = metaObject; candidate != nullptr; candidate = candidate->superClass()) {
if (!isInternalObject(candidate->className())) {
metaObject = candidate;
break;
{
const char *typeName = typeid(*cppSelf).name();
if (!Shiboken::Conversions::getConverter(typeName)) {
- auto *metaObject = metaObjectCandidate(cppSelf);
+ const auto *metaObject = metaObjectCandidate(cppSelf);
for (; metaObject != nullptr; metaObject = metaObject->superClass()) {
const char *name = metaObject->className();
if (Shiboken::Conversions::getConverter(name)) {
PyObject *getWrapperForQObject(QObject *cppSelf, PyTypeObject *sbk_type)
{
- PyObject *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppSelf));
+ auto *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppSelf));
if (pyOut) {
Py_INCREF(pyOut);
return pyOut;
}
}
- pyOut = Shiboken::Object::newObject(sbk_type, cppSelf, false, false, typeName(cppSelf));
+ pyOut = Shiboken::Object::newObjectWithHeuristics(sbk_type, cppSelf, false, typeName(cppSelf));
return pyOut;
}
QString pyStringToQString(PyObject *str)
{
if (str == Py_None)
- return QString();
+ return {};
if (PyUnicode_Check(str) != 0)
return pyUnicodeToQString(str);
if (PyBytes_Check(str)) {
- const char *asciiBuffer = PyBytes_AS_STRING(str);
+ const char *asciiBuffer = PyBytes_AsString(str);
if (asciiBuffer)
return QString::fromLatin1(asciiBuffer);
}
- return QString();
+ return {};
}
// PySide-1499: Provide an efficient, correct PathLike interface
// For empty constructors path can be nullptr
// fallback to an empty QString in that case.
if (!path)
- return QString();
+ return {};
// str or bytes pass through
if (PyUnicode_Check(path) || PyBytes_Check(path))
// Let PyOS_FSPath do its work and then fix the result for Windows.
Shiboken::AutoDecRef strPath(PyOS_FSPath(path));
if (strPath.isNull())
- return QString();
+ return {};
return QDir::fromNativeSeparators(pyStringToQString(strPath));
}
static void formatPyDict(PyObject *obj, QDebug &debug)
{
- PyObject *key;
- PyObject *value;
+ PyObject *key{};
+ PyObject *value{};
Py_ssize_t pos = 0;
bool first = true;
debug << '{';
return;
}
if (PyType_Check(obj) == 0)
- debug << pyTypeName(obj) << ": ";
+ debug << pyTypeName(obj);
+ const auto refs = Py_REFCNT(obj);
+ if (refs == UINT_MAX) // _Py_IMMORTAL_REFCNT
+ debug << ", immortal";
+ else
+ debug << ", refs=" << refs;
+ debug << ": ";
formatPyObjectValue(obj, debug);
}
Libs: -L${libdir} -lpyside6@pyside6_SUFFIX@@SHIBOKEN_PYTHON_CONFIG_SUFFIX@@LIBRARY_OUTPUT_SUFFIX@
Cflags: -I${includedir}
Requires: shiboken6
-
return nullptr;
}
- auto type = reinterpret_cast<PyTypeObject *>(arg);
+ auto *type = reinterpret_cast<PyTypeObject *>(arg);
if (checkMode != CheckMode::None && !Shiboken::ObjectType::checkType(type)) {
PyErr_Format(PyExc_TypeError,
{
int result = -1;
if (PyTuple_Size(args) == 1) {
- PyObject *arg = PyTuple_GET_ITEM(args, 0);
+ PyObject *arg = PyTuple_GetItem(args, 0);
if (PyUnicode_Check(arg)) {
auto *pData = DecoratorPrivate::get<StringDecoratorPrivate>(self);
result = 0;
int result = -1;
const auto argsCount = PyTuple_Size(args);
if (argsCount == 1) {
- PyObject *arg = PyTuple_GET_ITEM(args, 0);
+ PyObject *arg = PyTuple_GetItem(args, 0);
if (PyType_Check(arg)) {
result = 0;
auto *pData = DecoratorPrivate::get<TypeDecoratorPrivate>(self);
/// \param self self
/// \param args Arguments
/// \return 0 if the parameter is correct, else -1 (for tp_init())
- int convertToString(PyObject *self, PyObject *args);
+ static int convertToString(PyObject *self, PyObject *args);
private:
QByteArray m_string;
/// \param self self
/// \param args Arguments
/// \return 0 if the parameter is correct, else -1 (for tp_init())
- int convertToType(PyObject *self, PyObject *args);
+ static int convertToType(PyObject *self, PyObject *args);
private:
PyTypeObject *m_type = nullptr;
static void tp_free(void *self)
{
- auto pySelf = reinterpret_cast<PyObject *>(self);
- auto decorator = reinterpret_cast<PySideClassDecorator *>(self);
+ auto *pySelf = reinterpret_cast<PyObject *>(self);
+ auto *decorator = reinterpret_cast<PySideClassDecorator *>(self);
delete decorator->d;
PepExt_TypeCallFree(Py_TYPE(pySelf)->tp_base, self);
}
if (pData->m_alreadyWrapped)
return PyErr_Format(PyExc_TypeError, "This instance of ClassInfo() was already used to wrap an object");
- PyTypeObject *klassType = reinterpret_cast<PyTypeObject *>(klass);
+ auto *klassType = reinterpret_cast<PyTypeObject *>(klass);
if (!PySide::ClassInfo::setClassInfo(klassType, pData->m_data))
return PyErr_Format(PyExc_TypeError, "This decorator can only be used on classes that are subclasses of QObject");
PyObject *infoDict = nullptr;
auto size = PyTuple_Size(args);
if (size == 1 && kwds == nullptr) {
- PyObject *tmp = PyTuple_GET_ITEM(args, 0);
+ PyObject *tmp = PyTuple_GetItem(args, 0);
if (PyDict_Check(tmp))
infoDict = tmp;
} else if (size == 0 && kwds && PyDict_Check(kwds)) {
void functionFree(void *self)
{
- PySideMetaFunction *function = reinterpret_cast<PySideMetaFunction *>(self);
+ auto *function = reinterpret_cast<PySideMetaFunction *>(self);
delete function->d;
}
PyObject *functionCall(PyObject *self, PyObject *args, PyObject * /* kw */)
{
- PySideMetaFunction *function = reinterpret_cast<PySideMetaFunction *>(self);
+ auto *function = reinterpret_cast<PySideMetaFunction *>(self);
- PyObject *retVal;
+ PyObject *retVal{};
if (!PySide::MetaFunction::call(function->d->qobject, function->d->methodIndex, args, &retVal))
return nullptr;
return retVal;
// args given plus return type
Shiboken::AutoDecRef sequence(PySequence_Fast(args, nullptr));
- qsizetype numArgs = PySequence_Fast_GET_SIZE(sequence.object()) + 1;
+ qsizetype numArgs = PySequence_Size(sequence.object()) + 1;
if (numArgs - 1 > argTypes.size()) {
PyErr_Format(PyExc_TypeError, "%s only accepts %d argument(s), %d given!",
return false;
}
- QVariant *methValues = new QVariant[numArgs];
+ auto *methValues = new QVariant[numArgs];
void **methArgs = new void *[numArgs];
// Prepare room for return type
const char *returnType = method.typeName();
- if (returnType && std::strcmp("void", returnType))
+ if (returnType && std::strcmp("void", returnType) != 0)
argTypes.prepend(returnType);
else
argTypes.prepend(QByteArray());
methArgs[i] = methValues[i].data();
if (i == 0) // Don't do this for return type
continue;
+ Shiboken::AutoDecRef obj(PySequence_GetItem(sequence.object(), i - 1));
if (metaType.id() == QMetaType::QString) {
QString tmp;
- converter.toCpp(PySequence_Fast_GET_ITEM(sequence.object(), i - 1), &tmp);
+ converter.toCpp(obj, &tmp);
methValues[i] = tmp;
} else {
- converter.toCpp(PySequence_Fast_GET_ITEM(sequence.object(), i - 1), methArgs[i]);
+ converter.toCpp(obj, methArgs[i]);
}
} else {
PyErr_Format(PyExc_TypeError, "Unknown type used to call meta function (that may be a signal): %s", argTypes[i].constData());
}
} //namespace PySide::MetaFunction
-
PySidePropertyPrivate::PySidePropertyPrivate() noexcept = default;
PySidePropertyPrivate::~PySidePropertyPrivate() = default;
-PyObject *PySidePropertyPrivate::getValue(PyObject *source)
+PyObject *PySidePropertyPrivate::getValue(PyObject *source) const
{
if (fget) {
Shiboken::AutoDecRef args(PyTuple_New(1));
Py_INCREF(source);
- PyTuple_SET_ITEM(args, 0, source);
+ PyTuple_SetItem(args, 0, source);
return PyObject_CallObject(fget, args);
}
return nullptr;
{
if (fset && value) {
Shiboken::AutoDecRef args(PyTuple_New(2));
- PyTuple_SET_ITEM(args, 0, source);
- PyTuple_SET_ITEM(args, 1, value);
+ PyTuple_SetItem(args, 0, source);
+ PyTuple_SetItem(args, 1, value);
Py_INCREF(source);
Py_INCREF(value);
Shiboken::AutoDecRef result(PyObject_CallObject(fset, args));
}
if (fdel) {
Shiboken::AutoDecRef args(PyTuple_New(1));
- PyTuple_SET_ITEM(args, 0, source);
+ PyTuple_SetItem(args, 0, source);
Py_INCREF(source);
Shiboken::AutoDecRef result(PyObject_CallObject(fdel, args));
return (result.isNull() ? -1 : 0);
if (freset) {
Shiboken::AutoDecRef args(PyTuple_New(1));
Py_INCREF(source);
- PyTuple_SET_ITEM(args, 0, source);
+ PyTuple_SetItem(args, 0, source);
Shiboken::AutoDecRef result(PyObject_CallObject(freset, args));
return (result.isNull() ? -1 : 0);
}
static int qpropertyTpInit(PyObject *self, PyObject *args, PyObject *kwds)
{
PyObject *type{};
- auto data = reinterpret_cast<PySideProperty *>(self);
+ auto *data = reinterpret_cast<PySideProperty *>(self);
PySidePropertyPrivate *pData = data->d;
static const char *kwlist[] = {"type", "fget", "fset", "freset", "fdel", "doc", "notify",
Py_XINCREF(pData->pyTypeObject);
pData->typeName = PySide::Signal::getTypeName(type);
- if (pData->typeName.isEmpty())
+ if (type == Py_None || pData->typeName.isEmpty())
PyErr_SetString(PyExc_TypeError, "Invalid property type or type name.");
else if (pData->constant && ((pData->fset && pData->fset != Py_None)
|| (pData->notify && pData->notify != Py_None)))
static void qpropertyDeAlloc(PyObject *self)
{
qpropertyClear(self);
- if (PepRuntime_38_flag) {
- // PYSIDE-939: Handling references correctly.
- // This was not needed before Python 3.8 (Python issue 35810)
- Py_DECREF(Py_TYPE(self));
- }
+ // PYSIDE-939: Handling references correctly.
+ // This was not needed before Python 3.8 (Python issue 35810)
+ Py_DECREF(Py_TYPE(self));
PyObject_GC_UnTrack(self);
PepExt_TypeCallFree(self);
}
else
doc = pData->doc;
- auto notify = pData->notify ? pData->notify : Py_None;
+ auto *notify = pData->notify ? pData->notify : Py_None;
PyObject *obNew = PyObject_CallFunction(type, const_cast<char *>("OOOOOsO" "bbb" "bbb"),
pData->pyTypeObject, get, set, reset, del, doc.data(), notify,
static PyObject *qProperty_fget(PyObject *self, void *)
{
- auto func = reinterpret_cast<PySideProperty *>(self)->d->fget;
- auto ret = func != nullptr ? func : Py_None;
- Py_INCREF(ret);
- return ret;
+ auto *func = reinterpret_cast<PySideProperty *>(self)->d->fget;
+ if (func == nullptr)
+ Py_RETURN_NONE;
+ Py_INCREF(func);
+ return func;
}
static PyObject *qProperty_fset(PyObject *self, void *)
{
- auto func = reinterpret_cast<PySideProperty *>(self)->d->fset;
- auto ret = func != nullptr ? func : Py_None;
- Py_INCREF(ret);
- return ret;
+ auto *func = reinterpret_cast<PySideProperty *>(self)->d->fset;
+ if (func == nullptr)
+ Py_RETURN_NONE;
+ Py_INCREF(func);
+ return func;
}
static PyObject *qProperty_freset(PyObject *self, void *)
{
- auto func = reinterpret_cast<PySideProperty *>(self)->d->freset;
- auto ret = func != nullptr ? func : Py_None;
- Py_INCREF(ret);
- return ret;
+ auto *func = reinterpret_cast<PySideProperty *>(self)->d->freset;
+ if (func == nullptr)
+ Py_RETURN_NONE;
+ Py_INCREF(func);
+ return func;
}
static PyObject *qProperty_fdel(PyObject *self, void *)
{
- auto func = reinterpret_cast<PySideProperty *>(self)->d->fdel;
- auto ret = func != nullptr ? func : Py_None;
- Py_INCREF(ret);
- return ret;
+ auto *func = reinterpret_cast<PySideProperty *>(self)->d->fdel;
+ if (func == nullptr)
+ Py_RETURN_NONE;
+ Py_INCREF(func);
+ return func;
}
static PyObject *qPropertyDocGet(PyObject *self, void *)
{
- auto data = reinterpret_cast<PySideProperty *>(self);
+ auto *data = reinterpret_cast<PySideProperty *>(self);
PySidePropertyPrivate *pData = data->d;
QByteArray doc(pData->doc);
* subclass instance instead, otherwise it gets shadowed by
* __doc__ in the class's dict.
*/
- auto get_doc_obj = get_doc.object();
- int err = PyObject_SetAttr(self, PyMagicName::doc(), get_doc);
- return err < 0 ? nullptr : (Py_INCREF(get_doc_obj), get_doc_obj);
+ auto *get_doc_obj = get_doc.object();
+ if (PyObject_SetAttr(self, PyMagicName::doc(), get_doc) < 0)
+ return nullptr;
+ Py_INCREF(get_doc_obj);
+ return get_doc_obj;
}
PyErr_Clear();
}
static int qPropertyDocSet(PyObject *self, PyObject *value, void *)
{
- auto data = reinterpret_cast<PySideProperty *>(self);
+ auto *data = reinterpret_cast<PySideProperty *>(self);
PySidePropertyPrivate *pData = data->d;
if (String::check(value)) {
auto *attr = PyDict_GetItem(tpDict.object(), name);
if (!attr) {
PyObject *bases = type->tp_bases;
- const Py_ssize_t size = PyTuple_GET_SIZE(bases);
+ const Py_ssize_t size = PyTuple_Size(bases);
for (Py_ssize_t i = 0; i < size; ++i) {
- PyObject *base = PyTuple_GET_ITEM(bases, i);
+ PyObject *base = PyTuple_GetItem(bases, i);
attr = getFromType(reinterpret_cast<PyTypeObject *>(base), name);
if (attr)
return attr;
namespace PySide::Property {
static const char *Property_SignatureStrings[] = {
- "PySide6.QtCore.Property(self,type:type,fget:typing.Callable=None,fset:typing.Callable=None,"
- "freset:typing.Callable=None,fdel:typing.Callable=None,doc:str=None,"
- "notify:typing.Callable=None,designable:bool=True,scriptable:bool=True,"
+ "PySide6.QtCore.Property(self,type:type,"
+ "fget:typing.Optional[typing.Callable[[typing.Any],typing.Any]],"
+ "fset:typing.Optional[typing.Callable[[typing.Any,typing.Any],None]],"
+ "freset:typing.Optional[typing.Callable[[typing.Any,typing.Any],None]],"
+ "doc:str=None,"
+ "notify:typing.Optional[typing.Callable[[],None]],"
+ "designable:bool=True,scriptable:bool=True,"
"stored:bool=True,user:bool=False,constant:bool=False,final:bool=False)",
- "PySide6.QtCore.Property.deleter(self,fdel:typing.Callable)->PySide6.QtCore.Property",
- "PySide6.QtCore.Property.getter(self,fget:typing.Callable)->PySide6.QtCore.Property",
- "PySide6.QtCore.Property.read(self,fget:typing.Callable)->PySide6.QtCore.Property",
- "PySide6.QtCore.Property.setter(self,fset:typing.Callable)->PySide6.QtCore.Property",
- "PySide6.QtCore.Property.write(self,fset:typing.Callable)->PySide6.QtCore.Property",
- "PySide6.QtCore.Property.__call__(self, func:typing.Callable)->PySide6.QtCore.Property",
+ "PySide6.QtCore.Property.deleter(self,fdel:typing.Callable[[typing.Any],None])->PySide6.QtCore.Property",
+ "PySide6.QtCore.Property.getter(self,fget:typing.Callable[[typing.Any],typing.Any])->PySide6.QtCore.Property",
+ "PySide6.QtCore.Property.read(self,fget:typing.Callable[[typing.Any],typing.Any])->PySide6.QtCore.Property",
+ "PySide6.QtCore.Property.setter(self,fset:typing.Callable[[typing.Any,typing.Any],None])->PySide6.QtCore.Property",
+ "PySide6.QtCore.Property.write(self,fset:typing.Callable[[typing.Any,typing.Any],None])->PySide6.QtCore.Property",
+ "PySide6.QtCore.Property.__call__(self, func:typing.Callable[...,typing.Any])->PySide6.QtCore.Property",
nullptr}; // Sentinel
void init(PyObject *module)
virtual void metaCall(PyObject *source, QMetaObject::Call call, void **args);
- PyObject *getValue(PyObject *source);
+ PyObject *getValue(PyObject *source) const;
int setValue(PyObject *source, PyObject *value);
int reset(PyObject *source);
static Py_ssize_t get_lineno()
{
- PyObject *frame = reinterpret_cast<PyObject *>(PyEval_GetFrame()); // borrowed ref
+ auto *frame = reinterpret_cast<PyObject *>(PyEval_GetFrame()); // borrowed ref
AutoDecRef ob_lineno(PyObject_GetAttr(frame, Shiboken::PyName::f_lineno()));
if (ob_lineno.isNull() || !PyLong_Check(ob_lineno))
return -1;
static bool is_module_code()
{
- PyObject *frame = reinterpret_cast<PyObject *>(PyEval_GetFrame()); // borrowed ref
+ auto *frame = reinterpret_cast<PyObject *>(PyEval_GetFrame()); // borrowed ref
AutoDecRef ob_code(PyObject_GetAttr(frame, Shiboken::PyName::f_code()));
if (ob_code.isNull())
return false;
return nullptr;
if (bool(computedFlag) != flag) {
AutoDecRef name(PyObject_GetAttr(pyenum, PyMagicName::qualname()));
- auto cname = String::toCString(name);
+ const auto *cname = String::toCString(name);
const char *e = "Enum";
const char *f = "Flag";
PyErr_Format(PyExc_TypeError, "expected '%s' but got '%s' (%.200s)",
flag ? f : e, flag ? e : f, cname);
return nullptr;
}
- auto ok = analyzePyEnum(pyenum);
+ auto *ok = analyzePyEnum(pyenum);
if (ok == nullptr)
return nullptr;
if (is_module_code()) {
*/
if (enumCollector.empty())
return {};
- PyObject *obContainerType = reinterpret_cast<PyObject *>(containerType);
+ auto *obContainerType = reinterpret_cast<PyObject *>(containerType);
Py_ssize_t lineno = get_lineno();
std::vector<PyObject *> result;
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "pysideqslotobject_p.h"
+#include "dynamicslot_p.h"
-#include <autodecref.h>
#include <gilstate.h>
namespace PySide
{
+PySideQSlotObject::PySideQSlotObject(PyObject *callable,
+ const QByteArrayList ¶meterTypes,
+ const char *returnType) :
+ QtPrivate::QSlotObjectBase(&impl),
+ m_dynamicSlot(DynamicSlot::create(callable)),
+ m_parameterTypes(parameterTypes),
+ m_returnType(returnType)
+{
+}
+
+void PySideQSlotObject::call(void **args)
+{
+ Shiboken::GilState state;
+ m_dynamicSlot->call(m_parameterTypes, m_returnType, args);
+}
+
+PySideQSlotObject::~PySideQSlotObject() = default;
+
void PySideQSlotObject::impl(int which, QSlotObjectBase *this_, QObject *receiver,
void **args, bool *ret)
{
- auto self = static_cast<PySideQSlotObject *>(this_);
+ auto *self = static_cast<PySideQSlotObject *>(this_);
switch (which) {
case Destroy:
delete self;
break;
case Call:
- {
- Shiboken::GilState state;
- Shiboken::AutoDecRef arglist(PyTuple_New(0));
- Shiboken::AutoDecRef ret(PyObject_CallObject(self->callable, arglist));
- break;
- }
+ self->call(args);
+ break;
case Compare:
case NumOperations:
Q_UNUSED(receiver);
#include <QtCore/QObject>
#include <QtCore/qobjectdefs.h>
+#include <memory>
+
namespace PySide
{
+class DynamicSlot;
+
class PySideQSlotObject : public QtPrivate::QSlotObjectBase
{
- PyObject *callable;
+ Q_DISABLE_COPY_MOVE(PySideQSlotObject)
+public:
+ explicit PySideQSlotObject(PyObject *callable, const QByteArrayList ¶meterTypes,
+ const char *returnType = nullptr);
+ ~PySideQSlotObject();
+private:
static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **args, bool *ret);
+ void call(void **args);
-public:
- PySideQSlotObject(PyObject *callable) : QtPrivate::QSlotObjectBase(&impl), callable(callable)
- {
- Py_INCREF(callable);
- }
-
- ~PySideQSlotObject()
- {
- auto gstate = PyGILState_Ensure();
- Py_DECREF(callable);
- PyGILState_Release(gstate);
- }
+ std::unique_ptr<DynamicSlot> m_dynamicSlot;
+ const QByteArrayList m_parameterTypes;
+ const char *m_returnType;
};
#include "signalmanager.h"
#include <shiboken.h>
+#include <sbkstaticstrings.h>
#include <QtCore/QByteArray>
#include <QtCore/QDebug>
#include <QtCore/QMetaObject>
#include <pep384ext.h>
#include <signature.h>
+#include <sbkenum.h>
#include <algorithm>
#include <optional>
#include <utility>
#include <cstring>
-#define QT_SIGNAL_SENTINEL '2'
+static constexpr char QT_SIGNAL_SENTINEL = '2';
using namespace Qt::StringLiterals;
return debug;
}
+static bool isSourceDeleted(const PySideSignalInstance *p)
+{
+ return p->d == nullptr || p->d->shared->deleted;
+}
+
static bool connection_Check(PyObject *o)
{
if (o == nullptr || o == Py_None)
namespace PySide::Signal {
static QByteArray buildSignature(const QByteArray &, const QByteArray &);
- static void instanceInitialize(PySideSignalInstance *, PyObject *, PySideSignal *, PyObject *, int);
+ static void instanceInitialize(PySideSignalInstance *, PyObject *, PySideSignal *,
+ const PySideSignalInstanceSharedPtr &shared, int);
static PySideSignalData::Signature parseSignature(PyObject *);
static PyObject *buildQtCompatible(const QByteArray &);
} // PySide::Signal
return SbkType_FromSpec(&PySideMetaSignalType_spec);
}
-static PyTypeObject *PySideMetaSignal_TypeF(void)
+static PyTypeObject *PySideMetaSignal_TypeF()
{
static auto *type = createMetaSignalType();
return type;
static PyObject *signalInstanceRepr(PyObject *obSelf)
{
auto *self = reinterpret_cast<PySideSignalInstance *>(obSelf);
- auto *typeName = Py_TYPE(obSelf)->tp_name;
+ const auto *typeName = Py_TYPE(obSelf)->tp_name;
return Shiboken::String::fromFormat("<%s %s at %p>", typeName,
self->d ? self->d->signature.constData()
: "(no signature)", obSelf);
return -1;
bool tupledArgs = false;
- PySideSignal *self = reinterpret_cast<PySideSignal *>(obSelf);
+ auto *self = reinterpret_cast<PySideSignal *>(obSelf);
if (!self->data)
self->data = new PySideSignalData;
if (argName)
self->data->signalArguments = argumentNamesOpt.value();
for (Py_ssize_t i = 0, i_max = PyTuple_Size(args); i < i_max; i++) {
- PyObject *arg = PyTuple_GET_ITEM(args, i);
+ PyObject *arg = PyTuple_GetItem(args, i);
if (PySequence_Check(arg) && !Shiboken::String::check(arg) && !PyEnumMeta_Check(arg)) {
tupledArgs = true;
self->data->signatures.append(PySide::Signal::parseSignature(arg));
static void signalFree(void *vself)
{
- auto pySelf = reinterpret_cast<PyObject *>(vself);
- auto self = reinterpret_cast<PySideSignal *>(vself);
+ auto *pySelf = reinterpret_cast<PyObject *>(vself);
+ auto *self = reinterpret_cast<PySideSignal *>(vself);
if (self->data) {
delete self->data;
self->data = nullptr;
static PyObject *signalGetItem(PyObject *obSelf, PyObject *key)
{
- auto self = reinterpret_cast<PySideSignal *>(obSelf);
+ auto *self = reinterpret_cast<PySideSignal *>(obSelf);
QByteArray sigKey;
if (key) {
sigKey = PySide::Signal::parseSignature(key).signature;
static PyObject *signalToString(PyObject *obSelf)
{
- auto self = reinterpret_cast<PySideSignal *>(obSelf);
+ auto *self = reinterpret_cast<PySideSignal *>(obSelf);
QByteArray result;
if (self->data == nullptr || self->data->signatures.isEmpty()) {
result = "<invalid>"_ba;
static PyObject *signalGetAttr(PyObject *obSelf, PyObject *name)
{
- auto self = reinterpret_cast<PySideSignal *>(obSelf);
+ auto *self = reinterpret_cast<PySideSignal *>(obSelf);
if (PyUnicode_CompareWithASCIIString(name, "signatures") != 0)
return PyObject_GenericGetAttr(obSelf, name);
static void signalInstanceFree(void *vself)
{
- auto pySelf = reinterpret_cast<PyObject *>(vself);
- auto self = reinterpret_cast<PySideSignalInstance *>(vself);
+ auto *pySelf = reinterpret_cast<PyObject *>(vself);
+ auto *self = reinterpret_cast<PySideSignalInstance *>(vself);
PySideSignalInstancePrivate *dataPvt = self->d;
if (dataPvt) {
Py_DECREF(dataPvt->next);
dataPvt->next = nullptr;
}
+ dataPvt->shared->deleted = true;
delete dataPvt;
self->d = nullptr;
}
- self->deleted = true;
PepExt_TypeCallFree(Py_TYPE(pySelf)->tp_base, self);
}
return nullptr;
}
+static PySideSignalInstance *findSignalInstanceForSlot(PySideSignalInstance *source,
+ PyObject *slot)
+{
+ Q_ASSERT(slot != nullptr && slot != Py_None);
+
+ // Check signature of the slot (method or function) to match signal
+ const auto args = extractFunctionArgumentsFromSlot(slot);
+
+ if (args.function != nullptr && source->d->next != nullptr) {
+ auto slotArgRange = argCount(args);
+ if (args.isMethod) {
+ slotArgRange.min -= 1;
+ slotArgRange.max -= 1;
+ }
+
+ // Get signature args
+ // Iterate the possible types of connection for this signal and compare
+ // it with slot arguments
+ for (int slotArgs = slotArgRange.max; slotArgs >= slotArgRange.min; --slotArgs) {
+ if (auto *matchedSlot = findSignalInstance(source, slotArgs))
+ return matchedSlot;
+ }
+ }
+ return source;
+}
+
static PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject *kwds)
{
PyObject *slot = nullptr;
"O|O:SignalInstance", const_cast<char **>(kwlist), &slot, &type))
return nullptr;
- PySideSignalInstance *source = reinterpret_cast<PySideSignalInstance *>(self);
+ auto *source = reinterpret_cast<PySideSignalInstance *>(self);
if (!source->d)
return PyErr_Format(PyExc_RuntimeError, "cannot connect uninitialized SignalInstance");
- if (source->deleted)
+ if (isSourceDeleted(source))
return PyErr_Format(PyExc_RuntimeError, "Signal source has been deleted");
Shiboken::AutoDecRef pyArgs(PyList_New(0));
//find best match
while (sourceWalk && !match) {
- auto targetWalk = reinterpret_cast<PySideSignalInstance *>(slot);
+ auto *targetWalk = reinterpret_cast<PySideSignalInstance *>(slot);
while (targetWalk && !match) {
if (QMetaObject::checkConnectArgs(sourceWalk->d->signature,
targetWalk->d->signature)) {
- PyList_Append(pyArgs, sourceWalk->d->source);
+ PyList_Append(pyArgs, sourceWalk->d->shared->source);
Shiboken::AutoDecRef sourceSignature(PySide::Signal::buildQtCompatible(sourceWalk->d->signature));
PyList_Append(pyArgs, sourceSignature);
- PyList_Append(pyArgs, targetWalk->d->source);
+ PyList_Append(pyArgs, targetWalk->d->shared->source);
Shiboken::AutoDecRef targetSignature(PySide::Signal::buildQtCompatible(targetWalk->d->signature));
PyList_Append(pyArgs, targetSignature);
sourceWalk = reinterpret_cast<PySideSignalInstance *>(sourceWalk->d->next);
}
} else {
- // Check signature of the slot (method or function) to match signal
- const auto args = extractFunctionArgumentsFromSlot(slot);
- PySideSignalInstance *matchedSlot = nullptr;
-
- if (args.function != nullptr) {
- auto slotArgRange = argCount(args);
- if (args.isMethod) {
- slotArgRange.min -= 1;
- slotArgRange.max -= 1;
- }
-
- // Get signature args
- // Iterate the possible types of connection for this signal and compare
- // it with slot arguments
- for (int slotArgs = slotArgRange.max;
- slotArgs >= slotArgRange.min && matchedSlot == nullptr; --slotArgs) {
- matchedSlot = findSignalInstance(source, slotArgs);
- }
- }
-
// Adding references to pyArgs
- PyList_Append(pyArgs, source->d->source);
-
- if (matchedSlot != nullptr) {
- // If a slot matching the same number of arguments was found,
- // include signature to the pyArgs
- Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(matchedSlot->d->signature));
- PyList_Append(pyArgs, signature);
- } else {
- // Try the first by default if the slot was not found
- Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(source->d->signature));
- PyList_Append(pyArgs, signature);
- }
+ PyList_Append(pyArgs, source->d->shared->source);
+
+ // Check signature of the slot (method or function) to match signal
+ PySideSignalInstance *matchedSlot = findSignalInstanceForSlot(source, slot);
+ Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(matchedSlot->d->signature));
+ PyList_Append(pyArgs, signature);
PyList_Append(pyArgs, slot);
match = true;
}
if (match) {
Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs));
- Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source,
+ Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->shared->source,
PySide::PySideName::qtConnect()));
if (pyMethod.isNull()) // PYSIDE-79: check if pyMethod exists.
return PyErr_Format(PyExc_RuntimeError, "method 'connect' vanished!");
static int argCountInSignature(const char *signature)
{
- return QByteArray(signature).count(",") + 1;
+ return QByteArrayView{signature}.count(',') + 1;
}
static PyObject *signalInstanceEmit(PyObject *self, PyObject *args)
{
- PySideSignalInstance *source = reinterpret_cast<PySideSignalInstance *>(self);
+ auto *source = reinterpret_cast<PySideSignalInstance *>(self);
if (!source->d)
return PyErr_Format(PyExc_RuntimeError, "cannot emit uninitialized SignalInstance");
// PYSIDE-2201: Check if the object has vanished meanwhile.
// Tried to revive it without exception, but this gives problems.
- if (source->deleted)
+ if (isSourceDeleted(source))
return PyErr_Format(PyExc_RuntimeError, "The SignalInstance object was already deleted");
Shiboken::AutoDecRef pyArgs(PyList_New(0));
- int numArgsGiven = PySequence_Fast_GET_SIZE(args);
+ Py_ssize_t numArgsGiven = PySequence_Size(args);
int numArgsInSignature = argCountInSignature(source->d->signature);
// If number of arguments given to emit is smaller than the first source signature expects,
for (Py_ssize_t i = 0, max = PyTuple_Size(args); i < max; i++)
PyList_Append(pyArgs, PyTuple_GetItem(args, i));
- Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source,
+ Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->shared->source,
PySide::PySideName::qtEmit()));
Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs));
const auto sig = PySide::Signal::buildSignature(sigName, sigKey);
for (auto *data = firstSignal; data != nullptr; data = data->d->next) {
if (data->d->signature == sig) {
- PyObject *result = reinterpret_cast<PyObject *>(data);
+ auto *result = reinterpret_cast<PyObject *>(data);
Py_INCREF(result);
return result;
}
static inline void warnDisconnectFailed(PyObject *aSlot, const QByteArray &signature)
{
if (PyErr_Occurred() != nullptr) { // avoid "%S" invoking str() when an error is set.
+ PyObject *exc{};
+ PyObject *inst{};
+ PyObject *tb{};
+ PyErr_Fetch(&exc, &inst, &tb);
PyErr_WarnFormat(PyExc_RuntimeWarning, 0, "Failed to disconnect (%s) from signal \"%s\".",
Py_TYPE(aSlot)->tp_name, signature.constData());
+ PyErr_Restore(exc, inst, tb);
} else {
PyErr_WarnFormat(PyExc_RuntimeWarning, 0, "Failed to disconnect (%S) from signal \"%s\".",
aSlot, signature.constData());
static PyObject *signalInstanceDisconnect(PyObject *self, PyObject *args)
{
- auto source = reinterpret_cast<PySideSignalInstance *>(self);
+ auto *source = reinterpret_cast<PySideSignalInstance *>(self);
if (!source->d)
return PyErr_Format(PyExc_RuntimeError, "cannot disconnect uninitialized SignalInstance");
Shiboken::AutoDecRef pyArgs(PyList_New(0));
PyObject *slot = Py_None;
- if (PyTuple_Check(args) && PyTuple_GET_SIZE(args))
- slot = PyTuple_GET_ITEM(args, 0);
+ if (PyTuple_Check(args) && PyTuple_Size(args))
+ slot = PyTuple_GetItem(args, 0);
bool match = false;
if (Py_TYPE(slot) == PySideSignalInstance_TypeF()) {
- PySideSignalInstance *target = reinterpret_cast<PySideSignalInstance *>(slot);
+ auto *target = reinterpret_cast<PySideSignalInstance *>(slot);
if (QMetaObject::checkConnectArgs(source->d->signature, target->d->signature)) {
- PyList_Append(pyArgs, source->d->source);
+ PyList_Append(pyArgs, source->d->shared->source);
Shiboken::AutoDecRef source_signature(PySide::Signal::buildQtCompatible(source->d->signature));
PyList_Append(pyArgs, source_signature);
- PyList_Append(pyArgs, target->d->source);
+ PyList_Append(pyArgs, target->d->shared->source);
Shiboken::AutoDecRef target_signature(PySide::Signal::buildQtCompatible(target->d->signature));
PyList_Append(pyArgs, target_signature);
match = true;
PyList_Append(pyArgs, slot);
match = true;
} else {
- //try the first signature
- PyList_Append(pyArgs, source->d->source);
- Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(source->d->signature));
+ // try the matching signature, fall back to first
+ auto *matchedSlot = slot != Py_None ? findSignalInstanceForSlot(source, slot) : source;
+ PyList_Append(pyArgs, matchedSlot->d->shared->source);
+ Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(matchedSlot->d->signature));
PyList_Append(pyArgs, signature);
// disconnect all, so we need to use the c++ signature disconnect(qobj, signal, 0, 0)
if (match) {
Shiboken::AutoDecRef tupleArgs(PyList_AsTuple(pyArgs));
- Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->source,
+ Shiboken::AutoDecRef pyMethod(PyObject_GetAttr(source->d->shared->source,
PySide::PySideName::qtDisconnect()));
PyObject *result = PyObject_CallObject(pyMethod, tupleArgs);
if (result != Py_True)
// PYSIDE-68: Supply the missing __get__ function
static PyObject *signalDescrGet(PyObject *self, PyObject *obj, PyObject * /*type*/)
{
- auto signal = reinterpret_cast<PySideSignal *>(self);
+ auto *signal = reinterpret_cast<PySideSignal *>(self);
// Return the unbound signal if there is nothing to bind it to.
if (obj == nullptr || obj == Py_None
|| !PySide::isQObjectDerived(Py_TYPE(obj), true)) {
static PyObject *signalCall(PyObject *self, PyObject *args, PyObject *kw)
{
- auto signal = reinterpret_cast<PySideSignal *>(self);
+ auto *signal = reinterpret_cast<PySideSignal *>(self);
// Native C++ signals can't be called like functions, thus we throw an exception.
// The only way calling a signal can succeed (the Python equivalent of C++'s operator() )
// but walk through the whole mro to find a hidden method with the same name.
auto signalName = inst->d->signalName;
Shiboken::AutoDecRef name(Shiboken::String::fromCString(signalName));
- auto *mro = Py_TYPE(inst->d->source)->tp_mro;
- const Py_ssize_t n = PyTuple_GET_SIZE(mro);
+ auto *mro = Py_TYPE(inst->d->shared->source)->tp_mro;
+ const Py_ssize_t n = PyTuple_Size(mro);
for (Py_ssize_t idx = 0; idx < n; idx++) {
- auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
+ auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
Shiboken::AutoDecRef tpDict(PepType_GetDict(sub_type));
auto *hom = PyDict_GetItem(tpDict, name);
- PyObject *realFunc{};
- if (hom && PyCallable_Check(hom) && (realFunc = _getRealCallable(hom)))
- return realFunc;
+ if (hom != nullptr && PyCallable_Check(hom) != 0) {
+ if (auto *realFunc = _getRealCallable(hom))
+ return realFunc;
+ }
}
return nullptr;
}
return nullptr;
}
- Shiboken::AutoDecRef homonymousMethod(PepExt_Type_CallDescrGet(hom, PySideSignal->d->source,
+ Shiboken::AutoDecRef homonymousMethod(PepExt_Type_CallDescrGet(hom, PySideSignal->d->shared->source,
nullptr));
return PyObject_Call(homonymousMethod, args, kw);
}
// PYSIDE-1431: Walk the mro and update. But see PYSIDE-1751 below.
while ((mroItem.reset(PyIter_Next(mroIterator))), mroItem.object()) {
+ PyObject *key{};
+ PyObject *value{};
Py_ssize_t pos = 0;
- PyObject *key, *value;
auto *type = reinterpret_cast<PyTypeObject *>(mroItem.object());
Shiboken::AutoDecRef tpDict(PepType_GetDict(type));
while (PyDict_Next(tpDict, &pos, &key, &value)) {
auto *inst = PyObject_New(PySideSignalInstance, PySideSignalInstance_TypeF());
Shiboken::AutoDecRef signalInstance(reinterpret_cast<PyObject *>(inst));
auto *si = reinterpret_cast<PySideSignalInstance *>(signalInstance.object());
+ auto shared = std::make_shared<PySideSignalInstanceShared>();
+ shared->source = source;
instanceInitialize(si, key, reinterpret_cast<PySideSignal *>(value),
- source, 0);
+ shared, 0);
if (PyDict_SetItem(dict, key, signalInstance) == -1)
return; // An error occurred while setting the attribute
}
return;
}
+// PYSIDE-2840: For an enum registered in Qt, return the C++ name.
+// Ignore flags here; their underlying enums are of Python type flags anyways.
+static QByteArray getQtEnumTypeName(PyTypeObject *type)
+{
+ if (!Shiboken::Enum::checkType(type))
+ return {};
+
+ Shiboken::AutoDecRef qualName(PyObject_GetAttr(reinterpret_cast<PyObject *>(type),
+ Shiboken::PyMagicName::qualname()));
+ QByteArray result = Shiboken::String::toCString(qualName.object());
+ result.replace(".", "::");
+
+ const auto metaType = QMetaType::fromName(result);
+ return metaType.isValid() && metaType.flags().testFlag(QMetaType::IsEnumeration)
+ ? result : QByteArray{};
+}
+
QByteArray getTypeName(PyObject *obType)
{
if (PyType_Check(obType)) {
return QByteArrayLiteral("QVariantList");
if (type == &PyDict_Type)
return QByteArrayLiteral("QVariantMap");
- return QByteArrayLiteral("PyObject");
+ QByteArray enumName = getQtEnumTypeName(type);
+ return enumName.isEmpty() ? "PyObject"_ba : enumName;
}
if (obType == Py_None) // Must be checked before as Shiboken::String::check accepts Py_None
return voidType();
result = sizeof(qreal) == sizeof(double) ? "double" : "float";
return result;
}
- return QByteArray();
+ return {};
}
static QByteArray buildSignature(const QByteArray &name, const QByteArray &signature)
static void sourceGone(void *data)
{
- auto *self = reinterpret_cast<PySideSignalInstance *>(data);
- self->deleted = true;
+ auto *ptr = reinterpret_cast<PySideSignalInstanceSharedPtr *>(data);
+ (*ptr)->deleted = true;
+ delete ptr;
}
-static void instanceInitialize(PySideSignalInstance *self, PyObject *name, PySideSignal *signal, PyObject *source, int index)
+static void instanceInitialize(PySideSignalInstance *self, PyObject *name,
+ PySideSignal *signal,
+ const PySideSignalInstanceSharedPtr &shared,
+ int index)
{
self->d = new PySideSignalInstancePrivate;
- self->deleted = false;
PySideSignalInstancePrivate *selfPvt = self->d;
+ selfPvt->shared = shared;
+ // PYSIDE-2201: We have no reference to source. Let's take a weakref to get
+ // notified when source gets deleted.
+ if (index == 0) {
+ auto *ptr = new PySideSignalInstanceSharedPtr(shared);
+ PySide::WeakRef::create(shared->source, sourceGone, ptr);
+ }
+
selfPvt->next = nullptr;
if (signal->data->signalName.isEmpty())
signal->data->signalName = Shiboken::String::toCString(name);
selfPvt->signalName = signal->data->signalName;
- selfPvt->source = source;
const auto &signature = signal->data->signatures.at(index);
selfPvt->signature = buildSignature(self->d->signalName, signature.signature);
selfPvt->argCount = signature.argCount;
selfPvt->homonymousMethod = signal->homonymousMethod;
Py_INCREF(selfPvt->homonymousMethod);
}
- // PYSIDE-2201: We have no reference to source. Let's take a weakref to get
- // notified when source gets deleted.
- PySide::WeakRef::create(source, sourceGone, self);
index++;
if (index < signal->data->signatures.size()) {
selfPvt->next = PyObject_New(PySideSignalInstance, PySideSignalInstance_TypeF());
- instanceInitialize(selfPvt->next, name, signal, source, index);
+ instanceInitialize(selfPvt->next, name, signal, shared, index);
}
}
PySideSignalInstance *instance = PyObject_New(PySideSignalInstance,
PySideSignalInstance_TypeF());
- instanceInitialize(instance, name, self, object, 0);
- auto sbkObj = reinterpret_cast<SbkObject *>(object);
+ auto shared = std::make_shared<PySideSignalInstanceShared>();
+ shared->source = object;
+ instanceInitialize(instance, name, self, shared, 0);
+ auto *sbkObj = reinterpret_cast<SbkObject *>(object);
if (!Shiboken::Object::wasCreatedByPython(sbkObj))
Py_INCREF(object); // PYSIDE-79: this flag was crucial for a wrapper call.
return instance;
{
PySideSignalInstance *root = nullptr;
PySideSignalInstance *previous = nullptr;
+ auto shared = std::make_shared<PySideSignalInstanceShared>();
+ shared->source = source;
for (const QMetaMethod &m : methodList) {
PySideSignalInstance *item = PyObject_New(PySideSignalInstance, PySideSignalInstance_TypeF());
if (!root)
previous->d->next = item;
item->d = new PySideSignalInstancePrivate;
- item->deleted = false;
PySideSignalInstancePrivate *selfPvt = item->d;
- selfPvt->source = source;
+ selfPvt->shared = shared;
QByteArray cppName(m.methodSignature());
cppName.truncate(cppName.indexOf('('));
// separate SignalName
selfPvt->signalName = cppName;
selfPvt->signature = m.methodSignature();
- selfPvt->argCount = int(m.parameterCount());
+ selfPvt->argCount = short(m.parameterCount());
selfPvt->attributes = m.attributes();
selfPvt->homonymousMethod = nullptr;
selfPvt->next = nullptr;
static void _addSignalToWrapper(PyTypeObject *wrapperType, const char *signalName, PySideSignal *signal)
{
Shiboken::AutoDecRef tpDict(PepType_GetDict(wrapperType));
- auto typeDict = tpDict.object();
- PyObject *homonymousMethod;
- if ((homonymousMethod = PyDict_GetItemString(typeDict, signalName))) {
+ auto *typeDict = tpDict.object();
+ if (auto *homonymousMethod = PyDict_GetItemString(typeDict, signalName)) {
Py_INCREF(homonymousMethod);
signal->homonymousMethod = homonymousMethod;
}
PyObject *getObject(PySideSignalInstance *signal)
{
- return signal->d->source;
+ return signal->d->shared->source;
}
const char *getSignature(PySideSignalInstance *signal)
EmitterData result;
result.emitter = PySide::convertToQObject(getObject(signal), false);
if (result.emitter != nullptr) {
- auto *mo = result.emitter->metaObject();
+ const auto *mo = result.emitter->metaObject();
result.methodIndex = mo->indexOfMethod(getSignature(signal));
}
return result;
}
-QByteArrayList getArgsFromSignature(const char *signature, bool *isShortCircuit)
-{
- QByteArray qsignature = QByteArray(signature).trimmed();
- QByteArrayList result;
-
- if (isShortCircuit)
- *isShortCircuit = !qsignature.contains(u'(');
- if (qsignature.contains("()") || qsignature.contains("(void)"))
- return result;
- if (qsignature.endsWith(')')) {
- const auto paren = qsignature.indexOf('(');
- if (paren >= 0) {
- qsignature.chop(1);
- qsignature.remove(0, paren + 1);
- result = qsignature.split(u',');
- for (auto &type : result)
- type = type.trimmed();
- }
- }
- return result;
-}
-
-QByteArray getCallbackSignature(const char *signal, QObject *receiver,
+QByteArray getCallbackSignature(QMetaMethod signal, QObject *receiver,
PyObject *callback, bool encodeName)
{
QByteArray functionName;
}
#endif
} else if (PyCFunction_Check(callback)) {
- const PyCFunctionObject *funcObj = reinterpret_cast<const PyCFunctionObject *>(callback);
+ const auto *funcObj = reinterpret_cast<const PyCFunctionObject *>(callback);
functionName = PepCFunction_GET_NAMESTR(funcObj);
useSelf = PyCFunction_GET_SELF(funcObj) != nullptr ? 1 : 0;
const int flags = PyCFunction_GET_FLAGS(funcObj);
for (int i = 0, count = mo->methodCount(); i < count; ++i) {
QMetaMethod me = mo->method(i);
if ((strncmp(me.methodSignature(), prefix, prefix.size()) == 0) &&
- QMetaObject::checkConnectArgs(signal, me.methodSignature())) {
+ QMetaObject::checkConnectArgs(signal, me)) {
numArgs = me.parameterTypes().size() + useSelf;
break;
}
functionName = Shiboken::String::toCString(slotArgs.functionName);
Q_ASSERT(!functionName.isEmpty());
- bool isShortCircuit = false;
-
if (functionName.startsWith('<') && functionName.endsWith('>')) { // fix "<lambda>"
functionName[0] = '_';
functionName[functionName.size() - 1] = '_';
}
QByteArray signature = encodeName ? codeCallbackName(callback, functionName) : functionName;
- QByteArrayList args = getArgsFromSignature(signal, &isShortCircuit);
-
- if (!isShortCircuit) {
- signature.append(u'(');
- if (numArgs == -1)
- numArgs = std::numeric_limits<qsizetype>::max();
- while (!args.isEmpty() && (args.size() > (numArgs - useSelf)))
- args.removeLast();
- signature.append(args.join(','));
- signature.append(')');
+ QByteArrayList args = signal.parameterTypes();
+
+ signature.append(u'(');
+
+ int slotArgumentCount = signal.parameterCount();
+ if (numArgs != -1 && slotArgumentCount > (numArgs - useSelf))
+ slotArgumentCount = numArgs - useSelf;
+
+ for (int i = 0; i < slotArgumentCount; ++i) {
+ if (i > 0)
+ signature.append(',');
+ signature.append(signal.parameterTypeName(i));
}
+ signature.append(')');
+
return signature;
}
{
PyObject_HEAD
PySideSignalInstancePrivate *d;
- bool deleted;
};
}; // extern "C"
* @param encodeName Used to specify if the returned signature will be encoded with Qt signal/slot style
* @return Return the callback signature
**/
-PYSIDE_API QByteArray getCallbackSignature(const char *signal, QObject *receiver,
+PYSIDE_API QByteArray getCallbackSignature(QMetaMethod signal, QObject *receiver,
PyObject *callback, bool encodeName);
-
-/**
- * This function parses the signature and then returns a list of argument types.
- *
- * @param signature The signal signature
- * @param isShortCircuit If this is a shortCircuit(python<->python) signal
- * @return Return true if this is a Qt Signal, otherwise return false
- * @todo replace return type by QList<QByteArray>
- **/
-QByteArrayList getArgsFromSignature(const char *signature,
- bool *isShortCircuit = nullptr);
-
} // namespace PySide::Signal
#endif
#include <QtCore/QByteArray>
#include <QtCore/QList>
+#include <memory>
+
struct PySideSignalData
{
struct Signature
struct PySideSignalInstance;
}; //extern "C"
+struct PySideSignalInstanceShared
+{
+ PyObject *source = nullptr;
+ bool deleted = false;
+};
+
+using PySideSignalInstanceSharedPtr = std::shared_ptr<PySideSignalInstanceShared>;
+
struct PySideSignalInstancePrivate
{
QByteArray signalName;
QByteArray signature;
- PyObject *source = nullptr;
+ PySideSignalInstanceSharedPtr shared;
PyObject *homonymousMethod = nullptr;
PySideSignalInstance *next = nullptr;
unsigned short attributes = 0;
return -1;
}
- PySideSlot *data = reinterpret_cast<PySideSlot *>(self);
+ auto *data = reinterpret_cast<PySideSlot *>(self);
if (!data->slotData)
data->slotData = new SlotData;
for(Py_ssize_t i = 0, i_max = PyTuple_Size(args); i < i_max; i++) {
- PyObject *argType = PyTuple_GET_ITEM(args, i);
+ PyObject *argType = PyTuple_GetItem(args, i);
const auto typeName = PySide::Signal::getTypeName(argType);
if (typeName.isEmpty()) {
PyErr_Format(PyExc_TypeError, "Unknown signal argument type: %s", Py_TYPE(argType)->tp_name);
Py_INCREF(callback);
if (PyCallable_Check(callback)) {
- PySideSlot *data = reinterpret_cast<PySideSlot *>(self);
+ auto *data = reinterpret_cast<PySideSlot *>(self);
if (!data->slotData)
data->slotData = new SlotData;
static const char *Slot_SignatureStrings[] = {
"PySide6.QtCore.Slot(self,*types:type,name:str=nullptr,result:type=nullptr)",
- "PySide6.QtCore.Slot.__call__(self,function:typing.Callable)->typing.Any",
+ "PySide6.QtCore.Slot.__call__(self,function:typing.Callable[[typing.Any],typing.Any])->typing.Any",
nullptr}; // Sentinel
void init(PyObject *module)
static PyObject *CallableObject_call(PyObject *callable_object, PyObject *args, PyObject * /* kw */)
{
- PySideCallableObject *obj = reinterpret_cast<PySideCallableObject *>(callable_object);
+ auto *obj = reinterpret_cast<PySideCallableObject *>(callable_object);
obj->weakref_func(obj->user_data);
- Py_XDECREF(PyTuple_GET_ITEM(args, 0)); //kill weak ref object
+ Py_XDECREF(PyTuple_GetItem(args, 0)); //kill weak ref object
Py_RETURN_NONE;
}
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qobjectconnect.h"
+#include "dynamicslot_p.h"
#include "pysideqobject.h"
#include "pysideqslotobject_p.h"
#include "pysidesignal.h"
#include "pysideutils.h"
#include "signalmanager.h"
-#include "shiboken.h"
+#include <sbkstring.h>
+#include <sbkstaticstrings.h>
#include "basewrapper.h"
#include "autodecref.h"
// PYSIDE-1523: Each could be a compiled method or a normal method here, for the
// compiled ones we can use the attributes.
- PyObject *function1;
+ PyObject *function1{};
if (PyMethod_Check(otherMethod.object())) {
function1 = PyMethod_GET_FUNCTION(otherMethod.object());
} else {
// Not retaining a reference in line with what PyMethod_GET_FUNCTION does.
}
- PyObject *function2;
+ PyObject *function2{};
if (is_pymethod) {
function2 = PyMethod_GET_FUNCTION(method);
} else {
QObject *receiver = nullptr;
PyObject *self = nullptr;
QByteArray callbackSig;
- bool usingGlobalReceiver = false;
+ bool forceDynamicSlot = false;
int slotIndex = -1;
};
d.noquote();
d.nospace();
d << "GetReceiverResult(receiver=" << r.receiver << ", self=" << r.self
- << ", sig=\"" << r.callbackSig << "\", slotIndex=" << r.slotIndex
- << ", usingGlobalReceiver=" << r.usingGlobalReceiver << ')';
+ << ", forceDynamicSlot=" << r.forceDynamicSlot
+ << ", sig=\"" << r.callbackSig << "\", slotIndex=" << r.slotIndex << ')';
return d;
}
#endif // QT_NO_DEBUG_STREAM
static bool isDeclaredIn(PyObject *method, const char *className)
{
bool result = false;
- if (auto *qualifiedNameC = getQualifiedName(PyMethod_Function(method))) {
+ if (const auto *qualifiedNameC = getQualifiedName(PyMethod_Function(method))) {
std::string_view qualifiedName(qualifiedNameC);
if (const auto dot = qualifiedName.rfind('.'); dot != std::string::npos)
result = qualifiedName.substr(0, dot) == className;
return result;
}
-static GetReceiverResult getReceiver(QObject *source, const char *signal,
- PyObject *callback)
+static GetReceiverResult getReceiver(QMetaMethod signal, PyObject *callback)
{
GetReceiverResult result;
- bool forceGlobalReceiver = false;
if (PyMethod_Check(callback)) {
result.self = PyMethod_GET_SELF(callback);
result.receiver = PySide::convertToQObject(result.self, false);
- forceGlobalReceiver = isMethodDecorator(callback, true, result.self);
+ // Prevent dynamic slot creation for decorators
+ result.forceDynamicSlot = isMethodDecorator(callback, true, result.self);
#ifdef PYPY_VERSION
} else if (Py_TYPE(callback) == PepBuiltinMethod_TypePtr) {
result.self = PyObject_GetAttrString(callback, "__self__");
result.self = PyObject_GetAttr(callback, Shiboken::PyName::im_self());
Py_DECREF(result.self);
result.receiver = PySide::convertToQObject(result.self, false);
- forceGlobalReceiver = isMethodDecorator(callback, false, result.self);
+ // Prevent dynamic slot creation for decorators
+ result.forceDynamicSlot = isMethodDecorator(callback, false, result.self);
} else if (PyCallable_Check(callback)) {
// Ok, just a callable object
result.receiver = nullptr;
result.self = nullptr;
}
- result.usingGlobalReceiver = !result.receiver || forceGlobalReceiver;
+ result.callbackSig =
+ PySide::Signal::getCallbackSignature(signal, result.receiver, callback,
+ false);
// Check if this callback is a overwrite of a non-virtual Qt slot (pre-Jira bug 1019).
// Make it possible to connect to a MyWidget.show() although QWidget.show()
// is a non-virtual slot which would be found by QMetaObject search.
// FIXME PYSIDE7: This is arguably a bit of a misguided "feature", remove?
- if (!result.usingGlobalReceiver && result.receiver && result.self) {
- result.callbackSig =
- PySide::Signal::getCallbackSignature(signal, result.receiver, callback,
- result.usingGlobalReceiver);
+ if (result.receiver && result.self) {
const QMetaObject *metaObject = result.receiver->metaObject();
result.slotIndex = metaObject->indexOfSlot(result.callbackSig.constData());
if (PyMethod_Check(callback) != 0 && result.slotIndex != -1
metaObject = metaObject->superClass();
// If the Python callback is not declared in the same class, assume it is
// a Python override. Resort to global receiver (PYSIDE-2418).
- if (!isDeclaredIn(callback, metaObject->className()))
- result.usingGlobalReceiver = true;
+ if (!isDeclaredIn(callback, metaObject->className())) {
+ result.receiver = nullptr;
+ result.slotIndex = -1;
+ }
}
}
- const auto receiverThread = result.receiver ? result.receiver->thread() : nullptr;
-
- if (result.usingGlobalReceiver) {
- PySide::SignalManager &signalManager = PySide::SignalManager::instance();
- result.receiver = signalManager.globalReceiver(source, callback, result.receiver);
- // PYSIDE-1354: Move the global receiver to the original receivers's thread
- // so that autoconnections work correctly.
- if (receiverThread && receiverThread != result.receiver->thread())
- result.receiver->moveToThread(receiverThread);
- result.callbackSig =
- PySide::Signal::getCallbackSignature(signal, result.receiver, callback,
- result.usingGlobalReceiver);
- const QMetaObject *metaObject = result.receiver->metaObject();
- result.slotIndex = metaObject->indexOfSlot(result.callbackSig.constData());
- }
-
return result;
}
return {};
// Extract receiver from callback
- const GetReceiverResult receiver = getReceiver(source, signal + 1, callback);
- if (receiver.receiver == nullptr && receiver.self == nullptr)
- return {};
-
- int slotIndex = receiver.slotIndex;
-
- PySide::SignalManager &signalManager = PySide::SignalManager::instance();
- if (slotIndex == -1) {
- if (!receiver.usingGlobalReceiver && receiver.self
- && !Shiboken::Object::hasCppWrapper(reinterpret_cast<SbkObject *>(receiver.self))) {
- qWarning("You can't add dynamic slots on an object originated from C++.");
- if (receiver.usingGlobalReceiver)
- signalManager.releaseGlobalReceiver(source, receiver.receiver);
-
- return {};
- }
-
- const char *slotSignature = receiver.callbackSig.constData();
- slotIndex = receiver.usingGlobalReceiver
- ? signalManager.globalReceiverSlotIndex(receiver.receiver, slotSignature)
- : PySide::SignalManager::registerMetaMethodGetIndex(receiver.receiver, slotSignature,
- QMetaMethod::Slot);
-
- if (slotIndex == -1) {
- if (receiver.usingGlobalReceiver)
- signalManager.releaseGlobalReceiver(source, receiver.receiver);
-
- return {};
- }
+ const QMetaMethod signalMethod = source->metaObject()->method(signalIndex);
+ GetReceiverResult receiver = getReceiver(signalMethod, callback);
+ if (!receiver.forceDynamicSlot && receiver.receiver != nullptr && receiver.slotIndex == -1) {
+ receiver.slotIndex = PySide::SignalManager::registerMetaMethodGetIndexBA(receiver.receiver,
+ receiver.callbackSig,
+ QMetaMethod::Slot);
}
QMetaObject::Connection connection{};
Py_BEGIN_ALLOW_THREADS // PYSIDE-2367, prevent threading deadlocks with connectNotify()
- connection = QMetaObject::connect(source, signalIndex, receiver.receiver, slotIndex, type);
+ if (!receiver.forceDynamicSlot && receiver.receiver != nullptr && receiver.slotIndex != -1) {
+ connection = QMetaObject::connect(source, signalIndex,
+ receiver.receiver, receiver.slotIndex, type);
+ } else {
+ auto parameterTypes = signalMethod.parameterTypes();
+ // Slots might have fewer arguments.
+ if (!receiver.callbackSig.isEmpty()) {
+ const auto paramCount = receiver.callbackSig.endsWith("()")
+ ? qsizetype(0) : receiver.callbackSig.count(',') + 1;
+ if (parameterTypes.size() > paramCount)
+ parameterTypes.resize(paramCount);
+ }
+ auto *slotObject = new PySideQSlotObject(callback,
+ parameterTypes,
+ signalMethod.typeName());
+ connection = QObjectPrivate::connect(source, signalIndex, slotObject, type);
+ }
Py_END_ALLOW_THREADS
- if (!connection) {
- if (receiver.usingGlobalReceiver)
- signalManager.releaseGlobalReceiver(source, receiver.receiver);
+ if (!connection)
return {};
- }
- Q_ASSERT(receiver.receiver);
- if (receiver.usingGlobalReceiver)
- signalManager.notifyGlobalReceiver(receiver.receiver);
+ registerSlotConnection(source, signalIndex, callback, connection);
- const QMetaMethod signalMethod = receiver.receiver->metaObject()->method(signalIndex);
static_cast<FriendlyQObject *>(source)->connectNotify(signalMethod);
return connection;
}
if (signalIndex == -1)
return {};
- // Extract receiver from callback
- const GetReceiverResult receiver = getReceiver(source, signal + 1, callback);
- if (receiver.receiver == nullptr && receiver.self == nullptr)
- return {};
-
- PySide::SignalManager &signalManager = PySide::SignalManager::instance();
-
- PySideQSlotObject *slotObject = new PySideQSlotObject(callback);
+ const QMetaMethod signalMethod = source->metaObject()->method(signalIndex);
+ auto *slotObject = new PySideQSlotObject(callback,
+ signalMethod.parameterTypes(),
+ signalMethod.typeName());
QMetaObject::Connection connection{};
Py_BEGIN_ALLOW_THREADS // PYSIDE-2367, prevent threading deadlocks with connectNotify()
connection = QObjectPrivate::connect(source, signalIndex, context, slotObject, type);
Py_END_ALLOW_THREADS
- if (!connection) {
- if (receiver.usingGlobalReceiver)
- signalManager.releaseGlobalReceiver(source, receiver.receiver);
+ if (!connection)
return {};
- }
-
- Q_ASSERT(receiver.receiver);
- if (receiver.usingGlobalReceiver)
- signalManager.notifyGlobalReceiver(receiver.receiver);
- const QMetaMethod signalMethod = receiver.receiver->metaObject()->method(signalIndex);
static_cast<FriendlyQObject *>(source)->connectNotify(signalMethod);
return connection;
}
if (!PySide::Signal::checkQtSignal(signal))
return false;
- // Extract receiver from callback
- const GetReceiverResult receiver = getReceiver(nullptr, signal, callback);
- if (receiver.receiver == nullptr && receiver.self == nullptr)
+ const auto *metaObject = source->metaObject();
+ const int signalIndex = metaObject->indexOfSignal(signal + 1);
+ if (signalIndex == -1)
return false;
- const int signalIndex = source->metaObject()->indexOfSignal(signal + 1);
- const int slotIndex = receiver.slotIndex;
-
- bool ok{};
- Py_BEGIN_ALLOW_THREADS // PYSIDE-2367, prevent threading deadlocks with disconnectNotify()
- ok = QMetaObject::disconnectOne(source, signalIndex, receiver.receiver, slotIndex);
- Py_END_ALLOW_THREADS
- if (!ok)
+ if (!disconnectSlot(source, signalIndex, callback))
return false;
- Q_ASSERT(receiver.receiver);
- const QMetaMethod slotMethod = receiver.receiver->metaObject()->method(slotIndex);
- static_cast<FriendlyQObject *>(source)->disconnectNotify(slotMethod);
+ const QMetaMethod signalMethod = metaObject->method(signalIndex);
+ static_cast<FriendlyQObject *>(source)->disconnectNotify(signalMethod);
+ return true;
+}
+
+bool callConnect(PyObject *self, const char *signal, PyObject *argument)
+{
+ using Shiboken::AutoDecRef;
- if (receiver.usingGlobalReceiver) { // might delete the receiver
- PySide::SignalManager &signalManager = PySide::SignalManager::instance();
- signalManager.releaseGlobalReceiver(source, receiver.receiver);
+ if (PyObject_TypeCheck(argument, PySideSignalInstance_TypeF()) == 0) {
+ AutoDecRef result(PyObject_CallMethod(self, "connect", "OsO", self, signal, argument));
+ return !result.isNull();
}
- return true;
+
+ // Connecting signal to signal
+ auto *signalInstance = reinterpret_cast<PySideSignalInstance *>(argument);
+ AutoDecRef signalSignature(Shiboken::String::fromFormat("2%s", PySide::Signal::getSignature(signalInstance)));
+ AutoDecRef result(PyObject_CallMethod(self, "connect", "OsOO", self, signal,
+ PySide::Signal::getObject(signalInstance),
+ signalSignature.object()));
+ return !result.isNull();
}
} // namespace PySide
PYSIDE_API bool qobjectDisconnectCallback(QObject *source, const char *signal,
PyObject *callback);
+/// Helper for functions that forward arguments to QObject::connect(),
+/// for example, QTimer::singleShot().
+PYSIDE_API bool callConnect(PyObject *self, const char *signal, PyObject *argument);
+
} // namespace PySide
#endif // QOBJECTCONNECT_H
#include "pysidelogging_p.h"
#include "pysideproperty.h"
#include "pysideproperty_p.h"
-#include "pysidecleanup.h"
#include "pyside_p.h"
#include "dynamicqmetaobject.h"
#include "pysidemetafunction_p.h"
-#include "pysidestaticstrings.h"
#include <autodecref.h>
#include <basewrapper.h>
#include <QtCore/QScopedPointer>
#include <QtCore/QTimerEvent>
-#include <algorithm>
-#include <limits>
#include <memory>
using namespace Qt::StringLiterals;
#if QSLOT_CODE != 1 || QSIGNAL_CODE != 2
#error QSLOT_CODE and/or QSIGNAL_CODE changed! change the hardcoded stuff to the correct value!
#endif
-#define PYSIDE_SLOT '1'
-#define PYSIDE_SIGNAL '2'
-#include "globalreceiverv2.h"
static PyObject *metaObjectAttr = nullptr;
-static PyObject *parseArguments(const QMetaMethod &method, void **args);
-static bool emitShortCircuitSignal(QObject *source, int signalIndex, PyObject *args);
-
-static bool qAppRunning = false;
static void destroyMetaObject(PyObject *obj)
{
void *ptr = PyCapsule_GetPointer(obj, nullptr);
- auto meta = reinterpret_cast<PySide::MetaObjectBuilder *>(ptr);
+ auto *meta = reinterpret_cast<PySide::MetaObjectBuilder *>(ptr);
SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(meta);
if (wrapper)
Shiboken::BindingManager::instance().releaseWrapper(wrapper);
static QByteArray methodSignature(const QMetaMethod &method)
{
QByteArray result;
- if (auto *t = method.typeName()) {
+ if (const auto *t = method.typeName()) {
result += t;
result += ' ';
}
return result;
}
-static QByteArray msgCannotConvertParameter(const QMetaMethod &method, qsizetype p)
+static QByteArray msgCannotConvertParameter(const QByteArray ¶meterTypeName,
+ const QByteArray &signature, qsizetype p)
{
- return "Cannot call meta function \""_ba + methodSignature(method)
+ return "Cannot call meta function \""_ba + signature
+ "\" because parameter " + QByteArray::number(p) + " of type \""_ba
- + method.parameterTypeName(p) + "\" cannot be converted."_ba;
+ + parameterTypeName + "\" cannot be converted."_ba;
+}
+
+static inline QByteArray msgCannotConvertParameter(QMetaMethod method, qsizetype p)
+{
+ return msgCannotConvertParameter(method.parameterTypeName(p),
+ methodSignature(method), p);
}
-static QByteArray msgCannotConvertReturn(const QMetaMethod &method)
+static QByteArray msgCannotConvertReturn(const QByteArray &signature)
{
- return "The return value of \""_ba + methodSignature(method) + "\" cannot be converted."_ba;
+ return "The return value of \""_ba + signature + "\" cannot be converted."_ba;
+}
+
+static inline QByteArray msgCannotConvertReturn(QMetaMethod method)
+{
+ return msgCannotConvertReturn(methodSignature(method));
}
namespace PySide {
const char *buff = nullptr;
Py_ssize_t size = 0;
if (PyBytes_Check(repr.object())) {
- buff = PyBytes_AS_STRING(repr.object());
- size = PyBytes_GET_SIZE(repr.object());
+ buff = PyBytes_AsString(repr.object());
+ size = PyBytes_Size(repr.object());
} else if (Shiboken::String::check(repr.object())) {
buff = Shiboken::String::toCString(repr.object());
size = Shiboken::String::len(repr.object());
};
-namespace PySide {
-using GlobalReceiverV2Ptr = std::shared_ptr<GlobalReceiverV2>;
-using GlobalReceiverV2Map = QHash<PySide::GlobalReceiverKey, GlobalReceiverV2Ptr>;
-}
-
using namespace PySide;
-// Listen for destroy() of main thread objects and ensure cleanup
-class SignalManagerDestroyListener : public QObject
-{
- Q_OBJECT
-public:
- Q_DISABLE_COPY_MOVE(SignalManagerDestroyListener)
-
- using QObject::QObject;
-
-public Q_SLOTS:
- void destroyNotify(const QObject *);
-
-protected:
- void timerEvent(QTimerEvent *event) override;
-
-private:
- int m_timerId = -1;
-};
-
-void SignalManagerDestroyListener::destroyNotify(const QObject *)
+struct SignalManagerPrivate
{
- if (qAppRunning && m_timerId == -1)
- m_timerId = startTimer(0);
-}
-
-void SignalManagerDestroyListener::timerEvent(QTimerEvent *event)
-{
- if (event->timerId() == m_timerId) {
- killTimer(std::exchange(m_timerId, -1));
- SignalManager::instance().purgeEmptyGlobalReceivers();
- }
-}
-
-struct SignalManager::SignalManagerPrivate
-{
- Q_DISABLE_COPY_MOVE(SignalManagerPrivate)
-
- SignalManagerPrivate() noexcept = default;
- ~SignalManagerPrivate() { clear(); }
-
- void deleteGlobalReceiver(const QObject *gr);
- void clear();
- void purgeEmptyGlobalReceivers();
-
- GlobalReceiverV2Map m_globalReceivers;
static SignalManager::QmlMetaCallErrorHandler m_qmlMetaCallErrorHandler;
static void handleMetaCallError(QObject *object, int *result);
static int qtPropertyMetacall(QObject *object, QMetaObject::Call call,
int id, void **args);
static int qtMethodMetacall(QObject *object, int id, void **args);
-
- QPointer<SignalManagerDestroyListener> m_listener;
};
SignalManager::QmlMetaCallErrorHandler
- SignalManager::SignalManagerPrivate::m_qmlMetaCallErrorHandler = nullptr;
-
-static void clearSignalManager()
-{
- PySide::SignalManager::instance().clear();
-}
+ SignalManagerPrivate::m_qmlMetaCallErrorHandler = nullptr;
static void PyObject_PythonToCpp_PyObject_PTR(PyObject *pyIn, void *cppOut)
{
}
static PyObject *PyObject_PTR_CppToPython_PyObject(const void *cppIn)
{
- auto pyOut = reinterpret_cast<PyObject *>(const_cast<void *>(cppIn));
+ auto *pyOut = reinterpret_cast<PyObject *>(const_cast<void *>(cppIn));
if (pyOut)
Py_INCREF(pyOut);
return pyOut;
}
-SignalManager::SignalManager() : m_d(new SignalManagerPrivate)
+void SignalManager::init()
{
// Register Qt primitive typedefs used on signals.
using namespace Shiboken;
Shiboken::Conversions::registerConverterName(converter, "PyObjectWrapper");
Shiboken::Conversions::registerConverterName(converter, "PySide::PyObjectWrapper");
- PySide::registerCleanupFunction(clearSignalManager);
-
if (!metaObjectAttr)
metaObjectAttr = Shiboken::String::fromCString("__METAOBJECT__");
}
-void SignalManager::clear()
-{
- m_d->clear();
-}
-
-SignalManager::~SignalManager()
-{
- delete m_d;
-}
-
-SignalManager &SignalManager::instance()
-{
- static SignalManager me;
- return me;
-}
-
void SignalManager::setQmlMetaCallErrorHandler(QmlMetaCallErrorHandler handler)
{
SignalManagerPrivate::m_qmlMetaCallErrorHandler = handler;
}
-static void qAppAboutToQuit()
-{
- qAppRunning = false;
- SignalManager::instance().purgeEmptyGlobalReceivers();
-}
-
-static bool isInMainThread(const QObject *o)
-{
- if (o->isWidgetType() || o->isWindowType() || o->isQuickItemType())
- return true;
- auto *app = QCoreApplication::instance();
- return app != nullptr && app->thread() == o->thread();
-}
-
-QObject *SignalManager::globalReceiver(QObject *sender, PyObject *callback, QObject *receiver)
-{
- if (m_d->m_listener.isNull() && !QCoreApplication::closingDown()) {
- if (auto *app = QCoreApplication::instance()) {
- // The signal manager potentially outlives QCoreApplication, ensure deletion
- m_d->m_listener = new SignalManagerDestroyListener(app);
- m_d->m_listener->setObjectName("qt_pyside_signalmanagerdestroylistener");
- QObject::connect(app, &QCoreApplication::aboutToQuit, qAppAboutToQuit);
- qAppRunning = true;
- }
- }
-
- auto &globalReceivers = m_d->m_globalReceivers;
- const GlobalReceiverKey key = GlobalReceiverV2::key(callback);
- auto it = globalReceivers.find(key);
- if (it == globalReceivers.end()) {
- auto gr = std::make_shared<GlobalReceiverV2>(callback, receiver);
- it = globalReceivers.insert(key, gr);
- }
-
- if (sender != nullptr) {
- it.value()->incRef(sender); // create a link reference
-
- // For main thread-objects, add a notification for destroy (PYSIDE-2646, 2141)
- if (qAppRunning && !m_d->m_listener.isNull() && isInMainThread(sender)) {
- QObject::connect(sender, &QObject::destroyed,
- m_d->m_listener, &SignalManagerDestroyListener::destroyNotify,
- Qt::UniqueConnection);
- }
- }
-
- return it.value().get();
-}
-
-void SignalManager::purgeEmptyGlobalReceivers()
-{
- m_d->purgeEmptyGlobalReceivers();
-}
-
-void SignalManager::notifyGlobalReceiver(QObject *receiver)
-{
- reinterpret_cast<GlobalReceiverV2 *>(receiver)->notify();
- purgeEmptyGlobalReceivers();
-}
-
-void SignalManager::releaseGlobalReceiver(const QObject *source, QObject *receiver)
-{
- auto gr = static_cast<GlobalReceiverV2 *>(receiver);
- gr->decRef(source);
- if (gr->isEmpty())
- m_d->deleteGlobalReceiver(gr);
-}
-
-void SignalManager::deleteGlobalReceiver(const QObject *gr)
-{
- SignalManager::instance().m_d->deleteGlobalReceiver(gr);
-}
-
-void SignalManager::SignalManagerPrivate::deleteGlobalReceiver(const QObject *gr)
-{
- for (auto it = m_globalReceivers.begin(), end = m_globalReceivers.end(); it != end; ++it) {
- if (it.value().get() == gr) {
- m_globalReceivers.erase(it);
- break;
- }
- }
-}
-
-void SignalManager::SignalManagerPrivate::clear()
-{
- // Delete receivers by always retrieving the current first element,
- // because deleting a receiver can indirectly delete another one
- // via ~DynamicSlotDataV2(). Using ~QHash/clear() could cause an
- // iterator invalidation, and thus undefined behavior.
- while (!m_globalReceivers.isEmpty())
- m_globalReceivers.erase(m_globalReceivers.cbegin());
-}
-
-static bool isEmptyGlobalReceiver(const GlobalReceiverV2Ptr &g)
-{
- return g->isEmpty();
-}
-
-void SignalManager::SignalManagerPrivate::purgeEmptyGlobalReceivers()
-{
- // Delete repetitively (see comment in clear()).
- while (true) {
- auto it = std::find_if(m_globalReceivers.cbegin(), m_globalReceivers.cend(),
- isEmptyGlobalReceiver);
- if (it == m_globalReceivers.cend())
- break;
- m_globalReceivers.erase(it);
- }
-}
-
-int SignalManager::globalReceiverSlotIndex(QObject *receiver, const char *signature) const
-{
- return static_cast<GlobalReceiverV2 *>(receiver)->addSlot(signature);
-}
-
bool SignalManager::emitSignal(QObject *source, const char *signal, PyObject *args)
{
if (!Signal::checkQtSignal(signal))
signal++;
int signalIndex = source->metaObject()->indexOfSignal(signal);
- if (signalIndex != -1) {
- // cryptic but works!
- // if the signature doesn't have a '(' it's a shor circuited signal, i.e. std::find
- // returned the string null terminator.
- bool isShortCircuit = !*std::find(signal, signal + std::strlen(signal), '(');
- return isShortCircuit
- ? emitShortCircuitSignal(source, signalIndex, args)
- : MetaFunction::call(source, signalIndex, args);
- }
- return false;
+ return signalIndex != -1 && MetaFunction::call(source, signalIndex, args);
}
// Handle errors from meta calls. Requires GIL and PyErr_Occurred()
-void SignalManager::SignalManagerPrivate::handleMetaCallError(QObject *object, int *result)
+void SignalManagerPrivate::handleMetaCallError(QObject *object, int *result)
{
// Bubbles Python exceptions up to the Javascript engine, if called from one
if (m_qmlMetaCallErrorHandler) {
if (idOpt.has_value())
*result = idOpt.value();
}
+ SignalManager::handleMetaCallError();
+}
+void SignalManager::handleMetaCallError()
+{
const int reclimit = Py_GetRecursionLimit();
// Inspired by Python's errors.c: PyErr_GivenExceptionMatches() function.
// Temporarily bump the recursion limit, so that PyErr_Print will not raise a recursion
}
// Handler for QMetaObject::ReadProperty/WriteProperty/ResetProperty:
-int SignalManager::SignalManagerPrivate::qtPropertyMetacall(QObject *object,
- QMetaObject::Call call,
- int id, void **args)
+int SignalManagerPrivate::qtPropertyMetacall(QObject *object,
+ QMetaObject::Call call,
+ int id, void **args)
{
const QMetaObject *metaObject = object->metaObject();
int result = id - metaObject->propertyCount();
if (PyErr_Occurred()) {
// PYSIDE-2160: An unknown type was reported. Indicated by StopIteration.
if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
- PyObject *excType, *excValue, *excTraceback;
+ PyObject *excType{};
+ PyObject *excValue{};
+ PyObject *excTraceback{};
PyErr_Fetch(&excType, &excValue, &excTraceback);
bool ign = call == QMetaObject::WriteProperty;
PyErr_WarnFormat(PyExc_RuntimeWarning, 0,
}
// Handler for QMetaObject::InvokeMetaMethod
-int SignalManager::SignalManagerPrivate::qtMethodMetacall(QObject *object,
- int id, void **args)
+int SignalManagerPrivate::qtMethodMetacall(QObject *object,
+ int id, void **args)
{
const QMetaObject *metaObject = object->metaObject();
const QMetaMethod method = metaObject->method(id);
PyErr_Format(PyExc_AttributeError, "Slot '%s::%s' not found.",
metaObject->className(), method.methodSignature().constData());
} else {
- SignalManager::callPythonMetaMethod(method, args, pyMethod, false);
+ SignalManager::callPythonMetaMethod(method, args, pyMethod);
}
}
// WARNING Isn't safe to call any metaObject and/or object methods beyond this point
// because the object can be deleted inside the called slot.
- if (gil.get() == nullptr)
+ if (gil == nullptr)
gil.reset(new Shiboken::GilState);
if (PyErr_Occurred())
return id;
}
-int SignalManager::callPythonMetaMethod(const QMetaMethod &method, void **args, PyObject *pyMethod, bool isShortCuit)
+// Helper for calling a Python pyCallable matching a Qt signal / slot.
+enum CallResult : int
{
- Q_ASSERT(pyMethod);
+ CallOk,
+ CallOtherError, // Python error set
+ CallReturnValueError,
+ CallArgumentError // Argument (return - CallArgumentError) caused an error
+};
- Shiboken::GilState gil;
- PyObject *pyArguments = isShortCuit
- ? reinterpret_cast<PyObject *>(args[1]) : parseArguments(method, args);
-
- if (pyArguments) {
- QScopedPointer<Shiboken::Conversions::SpecificConverter> retConverter;
- const char *returnType = method.typeName();
- if (returnType != nullptr && returnType[0] != 0 && std::strcmp("void", returnType) != 0) {
- retConverter.reset(new Shiboken::Conversions::SpecificConverter(returnType));
- if (!retConverter->isValid()) {
- PyErr_SetString(PyExc_RuntimeError, msgCannotConvertReturn(method).constData());
- return -1;
- }
- }
+static inline bool isNonVoidReturn(const char *returnType)
+{
+ return returnType != nullptr && returnType[0] != 0 && std::strcmp("void", returnType) != 0;
+}
+
+static int callPythonMetaMethodHelper(const QByteArrayList ¶mTypes,
+ const char *returnType /* = nullptr */,
+ void **args, PyObject *pyCallable)
+{
+ const qsizetype argsSize = paramTypes.size();
+ Shiboken::AutoDecRef preparedArgs(PyTuple_New(argsSize));
+
+ for (qsizetype i = 0; i < argsSize; ++i) {
+ void *data = args[i + 1];
+ auto param = paramTypes.at(i);
+ Shiboken::Conversions::SpecificConverter converter(param.constData());
+ if (!converter.isValid())
+ return CallResult::CallArgumentError + int(i);
+ PyTuple_SetItem(preparedArgs, i, converter.toPython(data));
+ }
+
+ QScopedPointer<Shiboken::Conversions::SpecificConverter> retConverter;
+ if (isNonVoidReturn(returnType)) {
+ retConverter.reset(new Shiboken::Conversions::SpecificConverter(returnType));
+ if (!retConverter->isValid())
+ return CallResult::CallReturnValueError;
+ }
- Shiboken::AutoDecRef retval(PyObject_CallObject(pyMethod, pyArguments));
+ Shiboken::AutoDecRef retval(PyObject_CallObject(pyCallable, preparedArgs.object()));
+ if (PyErr_Occurred() != nullptr || retval.isNull())
+ return CallResult::CallOtherError;
- if (!isShortCuit && pyArguments)
- Py_DECREF(pyArguments);
+ if (retval != Py_None && !retConverter.isNull())
+ retConverter->toCpp(retval, args[0]);
+ return CallResult::CallOk;
+}
- if (!retval.isNull() && retval != Py_None && !PyErr_Occurred() && retConverter)
- retConverter->toCpp(retval, args[0]);
+int SignalManager::callPythonMetaMethod(QMetaMethod method, void **args,
+ PyObject *callable)
+{
+ Q_ASSERT(callable);
+
+ Shiboken::GilState gil;
+ int callResult = callPythonMetaMethodHelper(method.parameterTypes(),
+ method.typeName(), args, callable);
+ switch (callResult) {
+ case CallOk:
+ return 0;
+ case CallOtherError:
+ return -1;
+ case CallReturnValueError:
+ PyErr_SetString(PyExc_RuntimeError, msgCannotConvertReturn(method).constData());
+ return -1;
+ default: { // CallArgumentError + n
+ const int arg = callResult - CallArgumentError;
+ PyErr_SetString(PyExc_TypeError, msgCannotConvertParameter(method, arg).constData());
+ return -1;
}
+ }
+ return 0;
+}
+
+static QByteArray signature(const char *name, const QByteArrayList ¶meterTypes,
+ const char *returnType)
+{
+ QByteArray result;
+ if (isNonVoidReturn(returnType))
+ result += QByteArray(returnType) + ' ';
+ result += QByteArray(name) + '(' + parameterTypes.join(", ") + ')';
+ return result;
+}
- return -1;
+int SignalManager::callPythonMetaMethod(const QByteArrayList ¶meterTypes,
+ const char *returnType,
+ void **args, PyObject *callable)
+{
+ Q_ASSERT(callable);
+
+ Shiboken::GilState gil;
+ int callResult = callPythonMetaMethodHelper(parameterTypes, returnType, args, callable);
+ switch (callResult) {
+ case CallOk:
+ return 0;
+ case CallOtherError:
+ return -1;
+ case CallReturnValueError: {
+ const auto &sig = signature("slot", parameterTypes, returnType);
+ PyErr_SetString(PyExc_RuntimeError, msgCannotConvertReturn(sig).constData());
+ return -1;
+ }
+ default: { // CallArgumentError + n
+ const int arg = callResult - CallArgumentError;
+ const auto &sig = signature("slot", parameterTypes, returnType);
+ const auto &msg = msgCannotConvertParameter(parameterTypes.at(arg), sig, arg);
+ PyErr_SetString(PyExc_TypeError, msg.constData());
+ return -1;
+ }
+ }
+ return 0;
}
bool SignalManager::registerMetaMethod(QObject *source, const char *signature, QMetaMethod::MethodType type)
return debug;
}
-int SignalManager::registerMetaMethodGetIndex(QObject *source, const char *signature, QMetaMethod::MethodType type)
+static int addMetaMethod(QObject *source, const QByteArray &signature,
+ QMetaMethod::MethodType type)
{
- if (!source) {
- qWarning("SignalManager::registerMetaMethodGetIndex(\"%s\") called with source=nullptr.",
- signature);
+ const QMetaObject *metaObject = source->metaObject();
+ SbkObject *self = Shiboken::BindingManager::instance().retrieveWrapper(source);
+ if (!Shiboken::Object::hasCppWrapper(self)) {
+ qWarning().noquote().nospace() << __FUNCTION__
+ << ": Cannot add dynamic method \"" << signature << "\" (" << type
+ << ") to " << source << ": No Wrapper found.";
return -1;
}
- const QMetaObject *metaObject = source->metaObject();
- int methodIndex = metaObject->indexOfMethod(signature);
- // Create the dynamic signal is needed
- if (methodIndex == -1) {
- SbkObject *self = Shiboken::BindingManager::instance().retrieveWrapper(source);
- if (!Shiboken::Object::hasCppWrapper(self)) {
- qWarning().noquote().nospace() << __FUNCTION__
- << ": Cannot add dynamic method \"" << signature << "\" (" << type
- << ") to " << source << ": No Wrapper found.";
- return -1;
- }
- auto *pySelf = reinterpret_cast<PyObject *>(self);
- auto *dict = SbkObject_GetDict_NoRef(pySelf);
- MetaObjectBuilder *dmo = metaBuilderFromDict(dict);
-
- // Create a instance meta object
- if (!dmo) {
- dmo = new MetaObjectBuilder(Py_TYPE(pySelf), metaObject);
- PyObject *pyDmo = PyCapsule_New(dmo, nullptr, destroyMetaObject);
- PyObject_SetAttr(pySelf, metaObjectAttr, pyDmo);
- Py_DECREF(pyDmo);
- }
- if (type == QMetaMethod::Slot) {
- qCWarning(lcPySide).noquote().nospace()
- << "Warning: Registering dynamic slot \""
- << signature << "\" on \"" << source->metaObject()->className()
- << "\". Consider annotating with " << slotSignature(signature);
- }
+ auto *pySelf = reinterpret_cast<PyObject *>(self);
+ auto *dict = SbkObject_GetDict_NoRef(pySelf);
+ MetaObjectBuilder *dmo = metaBuilderFromDict(dict);
+ // Create a instance meta object
+ if (dmo == nullptr) {
+ dmo = new MetaObjectBuilder(Py_TYPE(pySelf), metaObject);
+ PyObject *pyDmo = PyCapsule_New(dmo, nullptr, destroyMetaObject);
+ PyObject_SetAttr(pySelf, metaObjectAttr, pyDmo);
+ Py_DECREF(pyDmo);
+ }
+
+ if (type == QMetaMethod::Slot) {
+ qCWarning(lcPySide).noquote().nospace()
+ << "Warning: Registering dynamic slot \""
+ << signature << "\" on \"" << source->metaObject()->className()
+ << "\". Consider annotating with " << slotSignature(signature);
+ }
+
+ return type == QMetaMethod::Signal ? dmo->addSignal(signature) : dmo->addSlot(signature);
+}
+
+static inline void warnNullSource(const char *signature)
+{
+ qWarning("SignalManager::registerMetaMethodGetIndex(\"%s\") called with source=nullptr.",
+ signature);
+}
+
+int SignalManager::registerMetaMethodGetIndex(QObject *source, const char *signature,
+ QMetaMethod::MethodType type)
+{
+ if (source == nullptr) {
+ warnNullSource(signature);
+ return -1;
+ }
+ const QMetaObject *metaObject = source->metaObject();
+ const int methodIndex = metaObject->indexOfMethod(signature);
+ // Create the dynamic signal if needed
+ return methodIndex != -1
+ ? methodIndex : addMetaMethod(source, QByteArray(signature), type);
+}
- return type == QMetaMethod::Signal
- ? dmo->addSignal(signature) : dmo->addSlot(signature);
+int SignalManager::registerMetaMethodGetIndexBA(QObject* source, const QByteArray &signature,
+ QMetaMethod::MethodType type)
+{
+ if (source == nullptr) {
+ warnNullSource(signature.constData());
+ return -1;
}
- return methodIndex;
+ const QMetaObject *metaObject = source->metaObject();
+ const int methodIndex = metaObject->indexOfMethod(signature.constData());
+ // Create the dynamic signal if needed
+ return methodIndex != -1
+ ? methodIndex : addMetaMethod(source, signature, type);
}
const QMetaObject *SignalManager::retrieveMetaObject(PyObject *self)
return builder->update();
}
-
-static PyObject *parseArguments(const QMetaMethod &method, void **args)
-{
- const auto ¶mTypes = method.parameterTypes();
- const qsizetype argsSize = paramTypes.size();
- PyObject *preparedArgs = PyTuple_New(argsSize);
-
- for (qsizetype i = 0; i < argsSize; ++i) {
- void *data = args[i+1];
- auto param = paramTypes.at(i);
- Shiboken::Conversions::SpecificConverter converter(param.constData());
- if (!converter) {
- PyErr_SetString(PyExc_TypeError, msgCannotConvertParameter(method, i).constData());
- Py_DECREF(preparedArgs);
- return nullptr;
- }
- PyTuple_SET_ITEM(preparedArgs, i, converter.toPython(data));
- }
- return preparedArgs;
-}
-
-static bool emitShortCircuitSignal(QObject *source, int signalIndex, PyObject *args)
-{
- void *signalArgs[2] = {nullptr, args};
- source->qt_metacall(QMetaObject::InvokeMetaMethod, signalIndex, signalArgs);
- return true;
-}
-
-#include "signalmanager.moc"
using QmlMetaCallErrorHandler = std::optional<int>(*)(QObject *object);
- static SignalManager& instance();
+ static void init();
static void setQmlMetaCallErrorHandler(QmlMetaCallErrorHandler handler);
- QObject* globalReceiver(QObject *sender, PyObject *callback, QObject *receiver = nullptr);
- void releaseGlobalReceiver(const QObject* sender, QObject* receiver);
- int globalReceiverSlotIndex(QObject* sender, const char* slotSignature) const;
- void notifyGlobalReceiver(QObject* receiver);
-
- bool emitSignal(QObject* source, const char* signal, PyObject* args);
+ static bool emitSignal(QObject* source, const char* signal, PyObject* args);
static int qt_metacall(QObject* object, QMetaObject::Call call, int id, void** args);
// Used to register a new signal/slot on QMetaobject of source.
- static bool registerMetaMethod(QObject* source, const char* signature, QMetaMethod::MethodType type);
- static int registerMetaMethodGetIndex(QObject* source, const char* signature, QMetaMethod::MethodType type);
+ static bool registerMetaMethod(QObject* source, const char* signature,
+ QMetaMethod::MethodType type);
+ static int registerMetaMethodGetIndex(QObject* source, const char *signature,
+ QMetaMethod::MethodType type);
+ static int registerMetaMethodGetIndexBA(QObject* source, const QByteArray &signature,
+ QMetaMethod::MethodType type);
// used to discovery metaobject
static const QMetaObject* retrieveMetaObject(PyObject* self);
- // Disconnect all signals managed by Globalreceiver
- void clear();
- void purgeEmptyGlobalReceivers();
-
- // Utility function to call a python method usign args received in qt_metacall
- static int callPythonMetaMethod(const QMetaMethod& method, void** args, PyObject* obj, bool isShortCuit);
-
- static void deleteGlobalReceiver(const QObject *globalReceiver);
-
-private:
- struct SignalManagerPrivate;
- SignalManagerPrivate* m_d;
-
- SignalManager();
- ~SignalManager();
+ // Utility function to call a python method using args received in qt_metacall
+ static int callPythonMetaMethod(QMetaMethod method, void **args, PyObject *callable);
+ static int callPythonMetaMethod(const QByteArrayList ¶meterTypes,
+ const char *returnType /* = nullptr */,
+ void **args, PyObject *callable);
+ static void handleMetaCallError();
};
}
extern "C" {
-static PyTypeObject *createPySideQmlAttachedType(void)
+static PyTypeObject *createPySideQmlAttachedType()
{
auto typeSlots =
PySide::ClassDecorator::Methods<PySideQmlAttachedPrivate>::typeSlots();
static const char methodName[] = "qmlAttachedProperties";
static PyObject *const pyMethodName = Shiboken::String::createStaticString(methodName);
- PyObject *attachingTypeObj = reinterpret_cast<PyObject *>(attachingType);
+ auto *attachingTypeObj = reinterpret_cast<PyObject *>(attachingType);
Shiboken::AutoDecRef pyResult(PyObject_CallMethodObjArgs(attachingTypeObj, pyMethodName,
attachingTypeObj /* self */,
converter.toPython(&o),
if (!info || info->attachedType == nullptr)
return result;
- auto *name = reinterpret_cast<PyTypeObject *>(t)->tp_name;
+ const auto *name = reinterpret_cast<PyTypeObject *>(t)->tp_name;
if (nextAttachingType >= MAX_ATTACHING_TYPES) {
qWarning("Unable to initialize attached type \"%s\": "
"The limit %d of attached types has been reached.",
extern "C" {
-static PyTypeObject *createPySideQmlExtendedType(void)
+static PyTypeObject *createPySideQmlExtendedType()
{
auto typeSlots =
PySide::ClassDecorator::Methods<PySideQmlExtendedPrivate>::typeSlots();
}
Shiboken::AutoDecRef args(PyTuple_New(1));
- PyTuple_SET_ITEM(args.object(), 0, pyObj);
+ PyTuple_SetItem(args.object(), 0, pyObj);
auto *extensionTypeObj = reinterpret_cast<PyObject *>(info->extensionType);
Shiboken::AutoDecRef pyResult(PyObject_Call(extensionTypeObj, args, nullptr));
if (pyResult.isNull() || PyErr_Occurred()) {
info->foreignType = data->type();
// Insert an alias to be used by the factory functions of Decorators like
// @QmlExtended and @QmlAttached.
- auto *foreignObj = reinterpret_cast<const PyObject *>(info->foreignType);
+ const auto *foreignObj = reinterpret_cast<const PyObject *>(info->foreignType);
PySide::Qml::insertQmlTypeInfoAlias(foreignObj, info);
Py_INCREF(klass);
extern "C" {
-static PyTypeObject *createPySideQmlForeignType(void)
+static PyTypeObject *createPySideQmlForeignType()
{
auto typeSlots =
PySide::ClassDecorator::Methods<PySideQmlForeignPrivate>::typeSlots();
"designable", "scriptable", "stored",
"user", "constant", "final",
nullptr};
- PySideProperty *pySelf = reinterpret_cast<PySideProperty *>(self);
+ auto *pySelf = reinterpret_cast<PySideProperty *>(self);
auto *data = static_cast<QmlListPropertyPrivate *>(pySelf->d);
Shiboken::AutoDecRef args(PyTuple_New(2));
PyTypeObject *qobjectType = qObjectType();
- PyTuple_SET_ITEM(args, 0,
+ PyTuple_SetItem(args, 0,
Shiboken::Conversions::pointerToPython(qobjectType, propList->object));
- PyTuple_SET_ITEM(args, 1,
+ PyTuple_SetItem(args, 1,
Shiboken::Conversions::pointerToPython(qobjectType, item));
auto *data = reinterpret_cast<QmlListPropertyPrivate *>(propList->data);
Shiboken::GilState state;
Shiboken::AutoDecRef args(PyTuple_New(1));
- PyTuple_SET_ITEM(args, 0,
+ PyTuple_SetItem(args, 0,
Shiboken::Conversions::pointerToPython(qObjectType(), propList->object));
auto *data = reinterpret_cast<QmlListPropertyPrivate *>(propList->data);
Shiboken::AutoDecRef args(PyTuple_New(2));
PyTypeObject *qobjectType = qObjectType();
- PyTuple_SET_ITEM(args, 0,
+ PyTuple_SetItem(args, 0,
Shiboken::Conversions::pointerToPython(qobjectType, propList->object));
auto *converter = Shiboken::Conversions::PrimitiveTypeConverter<qsizetype>();
- PyTuple_SET_ITEM(args, 1,
+ PyTuple_SetItem(args, 1,
Shiboken::Conversions::copyToPython(converter, &index));
auto *data = reinterpret_cast<QmlListPropertyPrivate *>(propList->data);
Shiboken::AutoDecRef retVal(PyObject_CallObject(data->at, args));
- QObject *result = 0;
+ QObject *result = nullptr;
if (PyErr_Occurred())
PyErr_Print();
else if (PyType_IsSubtype(Py_TYPE(retVal), data->type))
Shiboken::AutoDecRef args(PyTuple_New(1));
PyTypeObject *qobjectType = qObjectType();
- PyTuple_SET_ITEM(args, 0,
+ PyTuple_SetItem(args, 0,
Shiboken::Conversions::pointerToPython(qobjectType, propList->object));
auto *data = reinterpret_cast<QmlListPropertyPrivate *>(propList->data);
Shiboken::AutoDecRef args(PyTuple_New(3));
PyTypeObject *qobjectType = qObjectType();
- PyTuple_SET_ITEM(args, 0,
+ PyTuple_SetItem(args, 0,
Shiboken::Conversions::pointerToPython(qobjectType, propList->object));
auto *converter = Shiboken::Conversions::PrimitiveTypeConverter<qsizetype>();
- PyTuple_SET_ITEM(args, 1,
+ PyTuple_SetItem(args, 1,
Shiboken::Conversions::copyToPython(converter, &index));
- PyTuple_SET_ITEM(args, 2,
+ PyTuple_SetItem(args, 2,
Shiboken::Conversions::pointerToPython(qobjectType, value));
auto *data = reinterpret_cast<QmlListPropertyPrivate *>(propList->data);
Shiboken::AutoDecRef args(PyTuple_New(1));
PyTypeObject *qobjectType = qObjectType();
- PyTuple_SET_ITEM(args, 0,
+ PyTuple_SetItem(args, 0,
Shiboken::Conversions::pointerToPython(qobjectType, propList->object));
auto *data = reinterpret_cast<QmlListPropertyPrivate *>(propList->data);
if (call != QMetaObject::ReadProperty)
return;
- QObject *qobj;
+ QObject *qobj{};
PyTypeObject *qobjectType = qObjectType();
Shiboken::Conversions::pythonToCppPointer(qobjectType, source, &qobj);
QQmlListProperty<QObject> declProp(
}
static const char *PropertyList_SignatureStrings[] = {
- "PySide6.QtQml.ListProperty(self,type:type,append:typing.Callable,"
- "at:typing.Callable=None,clear:typing.Callable=None,count:typing.Callable=None)",
+ "PySide6.QtQml.ListProperty(self,type:type,"
+ "append:typing.Optional[typing.Callable[...,typing.Any]]=None,"
+ "at:typing.Optional[typing.Callable[...,typing.Any]]=None,"
+ "clear:typing.Optional[typing.Callable[...,typing.Any]]=None,"
+ "count:typing.Optional[typing.Callable[...,typing.Any]]=None)",
nullptr // Sentinel
};
if (engine->currentStackFrame == nullptr)
return {};
- PyObject *errType, *errValue, *errTraceback;
+ PyObject *errType{};
+ PyObject *errValue{};
+ PyObject *errTraceback{};
PyErr_Fetch(&errType, &errValue, &errTraceback);
// PYSIDE-464: The error is only valid before PyErr_Restore,
// PYSIDE-464: therefore we take local copies.
PyErr_Print(); // Note: PyErr_Print clears the error.
if (isSyntaxError)
- return engine->throwSyntaxError(errString);
- if (isTypeError)
- return engine->throwTypeError(errString);
- return engine->throwError(errString);
+ engine->throwSyntaxError(errString);
+ else if (isTypeError)
+ engine->throwTypeError(errString);
+ else
+ engine->throwError(errString);
+ return -1;
#else
Q_UNUSED(object);
qWarning("libpyside6qml was built without QML private API support, error handling will not work.");
QMutexLocker locker(&PySide::nextQObjectMemoryAddrMutex());
PySide::setNextQObjectMemoryAddr(memory);
Shiboken::GilState state;
- PyObject *obj = PyObject_CallObject(reinterpret_cast<PyObject *>(type), 0);
+ PyObject *obj = PyObject_CallObject(reinterpret_cast<PyObject *>(type), nullptr);
if (!obj || PyErr_Occurred())
PyErr_Print();
PySide::setNextQObjectMemoryAddr(nullptr);
// Check if o inherits from baseClass
static bool inheritsFrom(const QMetaObject *o, const char *baseClass)
{
- for (auto *base = o->superClass(); base ; base = base->superClass()) {
+ for (const auto *base = o->superClass(); base ; base = base->superClass()) {
if (qstrcmp(base->className(), baseClass) == 0)
return true;
}
static bool setClassInfo(PyTypeObject *type, const QByteArray &key, const QByteArray &value)
{
if (!PySide::ClassInfo::setClassInfo(type, key, value)) {
- PyErr_Format(PyExc_TypeError, "Setting class info \"%s\" to \"%s\" on \"%s\" failed.",
+ PyErr_Format(PyExc_TypeError, R"(Setting class info "%s" to "%s" on "%s" failed.)",
key.constData(), value.constData(), type->tp_name);
return false;
}
const QMetaObject *metaObject,
const QMetaObject *classInfoMetaObject = nullptr)
{
- PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
+ auto *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
if (classInfoMetaObject == nullptr)
classInfoMetaObject = metaObject;
static int qmlRegisterType(PyObject *pyObj, PyObject *pyClassInfoObj,
const ImportData &importData)
{
- PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
+ auto *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
if (!isQObjectDerived(pyObjType, true))
return -1;
{
Shiboken::GilState gil;
Shiboken::AutoDecRef args(PyTuple_New(1));
- PyTuple_SET_ITEM(args, 0,
+ PyTuple_SetItem(args, 0,
Shiboken::Conversions::pointerToPython(qQmlEngineType(), engine));
PyObject *retVal = PyObject_CallObject(data()->callable, args);
QObject *result = handleReturnValue(retVal);
const ImportData &importData,
const SingletonQObjectCreation &callback)
{
- PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
+ auto *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
if (!isQObjectDerived(pyObjType, true))
return -1;
const QMetaObject *metaObject = nullptr;
if (isQObject) {
- PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
+ auto *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
if (!isQObjectDerived(pyObjType, true))
return -1;
Shiboken::GilState gil;
AutoDecRef args(PyTuple_New(1));
- PyTuple_SET_ITEM(args, 0, Conversions::pointerToPython(
+ PyTuple_SetItem(args, 0, Conversions::pointerToPython(
qQmlEngineType(), engine));
AutoDecRef retVal(PyObject_CallObject(callback, args));
using namespace Shiboken;
// Check if the Python Type inherit from QObject
- PyTypeObject *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
+ auto *pyObjType = reinterpret_cast<PyTypeObject *>(pyObj);
if (!isQObjectDerived(pyObjType, true))
return -1;
const auto importDataO = getGlobalImportData(decoratorName);
if (!importDataO.has_value())
return nullptr;
- const auto importData = importDataO.value();
+ const auto &importData = importDataO.value();
int result{};
if (mode == RegisterMode::Singleton) {
if (argsCount == 0) {
result = 0; // QML-generated reason
} else if (argsCount == 1) {
- PyObject *arg = PyTuple_GET_ITEM(args, 0);
+ PyObject *arg = PyTuple_GetItem(args, 0);
result = arg == Py_None
? 0 // QML-generated reason
: convertToString(self, args);
target_compile_definitions(PySidePlugin PRIVATE -DQT_NO_KEYWORDS=1)
if(PYTHON_LIMITED_API)
- target_compile_definitions(PySidePlugin PRIVATE "-DPy_LIMITED_API=0x03050000")
+ target_compile_definitions(PySidePlugin PRIVATE "-DPy_LIMITED_API=0x03080000")
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
#include <QtCore/QVariant>
#include <string_view>
+#include <utility>
using namespace Qt::StringLiterals;
return ok;
}
+static std::pair<int, int> pythonVersion()
+{
+ // read environment set by pyside_tool.py
+ bool majorOk{};
+ bool minorOk{};
+ const int majorVersion = qEnvironmentVariableIntValue("PY_MAJOR_VERSION", &majorOk);
+ const int minorVersion = qEnvironmentVariableIntValue("PY_MINOR_VERSION", &minorOk);
+ if (majorOk && minorVersion)
+ return {majorVersion, minorVersion};
+ return {PY_MAJOR_VERSION, PY_MINOR_VERSION};
+}
+
static void initVirtualEnvironment()
{
static const char virtualEnvVar[] = "VIRTUAL_ENV";
- // As of Python 3.8/Windows, Python is no longer able to run stand-alone in
- // a virtualenv due to missing libraries. Add the path to the modules
- // instead. macOS seems to be showing the same issues.
+ // Since Python 3.8 (Windows, macOS), Python is no longer able to run stand
+ // -alone in a virtualenv due to missing libraries. Add the path to the modules
+ // instead.
const auto os = QOperatingSystemVersion::currentType();
- bool ok;
- int majorVersion = qEnvironmentVariableIntValue("PY_MAJOR_VERSION", &ok);
- int minorVersion = qEnvironmentVariableIntValue("PY_MINOR_VERSION", &ok);
- if (!ok) {
- majorVersion = PY_MAJOR_VERSION;
- minorVersion = PY_MINOR_VERSION;
- }
-
if (!qEnvironmentVariableIsSet(virtualEnvVar)
- || (os != QOperatingSystemVersion::MacOS && os != QOperatingSystemVersion::Windows)
- || (majorVersion == 3 && minorVersion < 8)) {
+ || (os != QOperatingSystemVersion::MacOS && os != QOperatingSystemVersion::Windows)) {
return;
}
case QOperatingSystemVersion::Windows:
pythonPath.append(virtualEnvPath + R"(\Lib\site-packages)");
break;
- case QOperatingSystemVersion::MacOS:
+ case QOperatingSystemVersion::MacOS: {
+ const auto version = pythonVersion();
pythonPath.append(virtualEnvPath + "/lib/python"_ba +
- QByteArray::number(majorVersion) + '.'
- + QByteArray::number(minorVersion)
+ QByteArray::number(version.first) + '.'
+ + QByteArray::number(version.second)
+ "/site-packages"_ba);
+ }
break;
default:
break;
}
Shiboken::AutoDecRef pyArgs(PyTuple_New(1));
- PyTuple_SET_ITEM(pyArgs.object(), 0, pyParent); // tuple will keep pyParent reference
+ PyTuple_SetItem(pyArgs.object(), 0, pyParent); // tuple will keep pyParent reference
// Call python constructor
auto *result = reinterpret_cast<SbkObject *>(PyObject_CallObject(m_pyObject, pyArgs));
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
major_version = "@pyside_MAJOR_VERSION@"
minor_version = "@pyside_MINOR_VERSION@"
# See libshiboken/CMakeLists.txt
if(PYTHON_LIMITED_API)
- target_compile_definitions(QtExampleIcons PRIVATE "-DPy_LIMITED_API=0x03050000")
+ target_compile_definitions(QtExampleIcons PRIVATE "-DPy_LIMITED_API=0x03080000")
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
int main(int argc, char *argv[])
{
#ifndef PYPY_VERSION
- Py_SetProgramName(L"module-test");
Py_Initialize();
#endif
PyInit_QtExampleIcons();
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for Q3DExtras'''
from helper.usesqapplication import UsesQApplication
-from PySide6.QtCore import (Property, QObject, QPropertyAnimation, QTimer, Signal, Slot)
-from PySide6.QtGui import (QGuiApplication, QMatrix4x4, QQuaternion, QVector3D, QWindow)
-from PySide6.Qt3DCore import (Qt3DCore)
-from PySide6.Qt3DRender import (Qt3DRender)
-from PySide6.Qt3DExtras import (Qt3DExtras)
+from PySide6.QtCore import Property, QObject, QTimer, Signal
+from PySide6.QtGui import QMatrix4x4, QQuaternion, QVector3D
+from PySide6.Qt3DCore import Qt3DCore
+from PySide6.Qt3DExtras import Qt3DExtras
class OrbitTransformController(QObject):
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
+
+'''Test cases for QtAsyncio'''
+
+import unittest
+import asyncio
+
+import PySide6.QtAsyncio as QtAsyncio
+
+
+class QAsyncioTestCaseBug2790(unittest.TestCase):
+
+ async def producer(self, products: list[str]):
+ while True:
+ products.append("product")
+ await asyncio.sleep(2)
+
+ async def task(self, outputs: list[str]):
+ products = []
+ asyncio.ensure_future(self.producer(products))
+ for _ in range(6):
+ try:
+ async with asyncio.timeout(0.5):
+ while len(products) == 0:
+ await asyncio.sleep(0)
+ outputs.append(products.pop(0))
+ except TimeoutError:
+ outputs.append("Timeout")
+
+ def test_timeout(self):
+ # The Qt event loop (and thus QtAsyncio) does not guarantee that events
+ # will be processed in the order they were posted, so there is two
+ # possible outputs for this test.
+ outputs_expected_1 = ["product", "Timeout", "Timeout", "Timeout", "Timeout", "product"]
+ outputs_expected_2 = ["product", "Timeout", "Timeout", "Timeout", "product", "Timeout"]
+
+ outputs_real = []
+
+ QtAsyncio.run(self.task(outputs_real), keep_running=False)
+
+ self.assertTrue(outputs_real in [outputs_expected_1, outputs_expected_2])
+
+
+if __name__ == '__main__':
+ unittest.main()
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
+
+"""Test cases for QtAsyncio"""
+
+import unittest
+import asyncio
+import sys
+
+import PySide6.QtAsyncio as QtAsyncio
+
+
+@unittest.skipIf(sys.version_info < (3, 11), "Requires ExceptionGroup")
+class QAsyncioTestCaseBug2799(unittest.TestCase):
+ async def job(self):
+ await asyncio.sleep(1)
+
+ async def main(self):
+ async with asyncio.TaskGroup() as tg:
+ tg.create_task(self.job())
+ raise RuntimeError()
+
+ def test_exception_group(self):
+ with self.assertRaises(ExceptionGroup):
+ QtAsyncio.run(self.main(), keep_running=False)
+
+
+if __name__ == "__main__":
+ unittest.main()
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QtAsyncio'''
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QtAsyncio'''
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QtAsyncio'''
import asyncio
import unittest
+import sys
import PySide6.QtAsyncio as QtAsyncio
+@unittest.skipIf(sys.version_info < (3, 11), "Requires ExceptionGroup")
class QAsyncioTestCaseCancelTaskGroup(unittest.TestCase):
+
+ """
+ PYSIDE-2644: If a task was cancelled, then a new future created from this
+ task should be cancelled as well. Otherwise, in some scenarios like a loop
+ inside the task and with bad timing, if the new future is not cancelled,
+ the task would continue running in this loop despite having been cancelled.
+ This bad timing can occur especially if the first future finishes very
+ quickly.
+ """
+
def setUp(self) -> None:
super().setUp()
# We only reach the end of the loop if the task is not cancelled.
for coro in coros:
try:
QtAsyncio.run(self.main(coro), keep_running=False)
- except ExceptionGroup as e:
+ except ExceptionGroup as e: # noqa: F821
self.assertEqual(len(e.exceptions), 1)
self.assertIsInstance(e.exceptions[0], RuntimeError)
self.assertFalse(self._loop_end_reached)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QtAsyncio'''
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QtAsyncio'''
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QtAsyncio'''
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QtAsyncio'''
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QtAsyncio'''
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
+
+"""Test cases for QtAsyncio"""
+
+import unittest
+import asyncio
+
+import PySide6.QtAsyncio as QtAsyncio
+
+
+class QAsyncioTestCaseUncancel(unittest.TestCase):
+ """ https://superfastpython.com/asyncio-cancel-task-cancellation """
+
+ async def worker(self, outputs: list[str]):
+ # Ensure the task always gets done.
+ while True:
+ try:
+ await asyncio.sleep(2)
+ outputs.append("Task sleep completed normally")
+ break
+ except asyncio.CancelledError:
+ outputs.append("Task is cancelled, ignore and try again")
+ asyncio.current_task().uncancel()
+
+ async def main(self, outputs: list[str]):
+ task = asyncio.create_task(self.worker(outputs))
+ # Allow the task to run briefly.
+ await asyncio.sleep(0.5)
+ task.cancel()
+ try:
+ await task
+ except asyncio.CancelledError:
+ outputs.append("Task was cancelled")
+
+ cancelling = task.cancelling()
+ self.assertEqual(cancelling, 0)
+ outputs.append(f"Task cancelling: {cancelling}")
+
+ cancelled = task.cancelled()
+ self.assertFalse(cancelled)
+ outputs.append(f"Task cancelled: {cancelled}")
+
+ done = task.done()
+ self.assertTrue(done)
+ outputs.append(f"Task done: {done}")
+
+ def test_uncancel(self):
+ outputs_expected = []
+ outputs_real = []
+
+ asyncio.run(self.main(outputs_real))
+ QtAsyncio.run(self.main(outputs_expected), keep_running=False)
+
+ self.assertIsNotNone(outputs_real)
+ self.assertEqual(outputs_real, outputs_expected)
+
+
+if __name__ == "__main__":
+ unittest.main()
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QBluetoothLocalDevice'''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QLowEnergyServiceData'''
{
- "files": ["qcharts_test.py"]
+ "files": ["qcharts_test.py",
+ "qcharts_numpy_test.py"]
}
#!/usr/bin/python
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QCharts/numpy'''
init_test_paths(False)
from helper.usesqapplication import UsesQApplication
-from PySide6.QtCore import QCoreApplication
from PySide6.QtCharts import QLineSeries
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QCharts'''
init_test_paths(False)
from helper.usesqapplication import UsesQApplication
-from PySide6.QtCore import QRect, QSize, QTimer
-from PySide6.QtGui import QGuiApplication, QScreen
+from PySide6.QtCore import QTimer
+from PySide6.QtGui import QGuiApplication
from PySide6.QtCharts import QChart, QChartView, QPieSeries
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
'''
Unit tests for attribute cache in Python 3
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test case for QObject.signalsBlocked() and blockSignal()'''
self.qfile.aboutToClose.connect(self.callback)
- self.assertTrue(self.qfile.open(QFile.ReadOnly))
+ self.assertTrue(self.qfile.open(QFile.OpenModeFlag.ReadOnly))
self.qfile.close()
self.assertTrue(self.called)
self.called = False
self.qfile.blockSignals(True)
- self.assertTrue(self.qfile.open(QFile.ReadOnly))
+ self.assertTrue(self.qfile.open(QFile.OpenModeFlag.ReadOnly))
self.qfile.close()
self.assertTrue(not self.called)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' unit test for BUG #1063 '''
self.temp_file = tempfile.NamedTemporaryFile(delete=False)
self.temp_file.close()
self.f = QFile(self.temp_file.name)
- self.f.open(QIODevice.WriteOnly)
+ self.f.open(QIODevice.OpenModeFlag.WriteOnly)
self.strings = ('foo', 'bar')
self.stream = QTextStream(self.f)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' unit test for BUG #1069 '''
class QDataStreamOpOverloadTestCase(unittest.TestCase):
def setUp(self):
self.ba = QByteArray()
- self.stream = QDataStream(self.ba, QIODevice.WriteOnly)
+ self.stream = QDataStream(self.ba, QIODevice.OpenModeFlag.WriteOnly)
def testIt(self):
self.stream << "hello"
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' unit test for BUG #1313 '''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test bug 324: http://bugs.openbossa.org/show_bug.cgi?id=324'''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
def testIt(self):
device = MyDevice("hello world\nhello again")
- device.open(QIODevice.ReadOnly)
+ device.open(QIODevice.OpenModeFlag.ReadOnly)
s = QTextStream(device)
self.assertEqual(s.readLine(), "hello world")
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
class MyEvent(QEvent):
def __init__(self, i):
- print("TYPE:", type(QEvent.User))
- super().__init__(QEvent.Type(QEvent.User))
+ print("TYPE:", type(QEvent.Type.User))
+ super().__init__(QEvent.Type(QEvent.Type.User))
self.i = i
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
""" Unittest for bug #515 """
""" http://bugs.openbossa.org/show_bug.cgi?id=515 """
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from init_paths import init_test_paths
init_test_paths(False)
-import PySide6
from PySide6.QtCore import QPoint, QPointF
from PySide6.QtCore import QLine, QLineF
from PySide6.QtCore import QSize, QSizeF
self.assertEqual((1, 2), p.toTuple())
def testQLineToTuple(self):
- l = QLine(1, 2, 3, 4)
- self.assertEqual((1, 2, 3, 4), l.toTuple())
+ line = QLine(1, 2, 3, 4)
+ self.assertEqual((1, 2, 3, 4), line.toTuple())
def testQLineFToTuple(self):
- l = QLineF(1, 2, 3, 4)
- self.assertEqual((1, 2, 3, 4), l.toTuple())
+ line = QLineF(1, 2, 3, 4)
+ self.assertEqual((1, 2, 3, 4), line.toTuple())
def testQSizeToTuple(self):
s = QSize(1, 2)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import functools
import os
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, Slot, Signal, SIGNAL
+from PySide6.QtCore import QObject, Slot, Signal
def log_exception():
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QEvent, Qt
-import PySide6
+from PySide6.QtCore import QEvent
TEST_EVENT_TYPE = QEvent.Type(QEvent.registerEventType())
class TestEnums(unittest.TestCase):
def testUserTypesValues(self):
- self.assertTrue(QEvent.User <= TestEvent.TestEventType <= QEvent.MaxUser)
- self.assertTrue(QEvent.User <= TEST_EVENT_TYPE <= QEvent.MaxUser)
+ self.assertTrue(QEvent.Type.User <= TestEvent.TestEventType <= QEvent.Type.MaxUser)
+ self.assertTrue(QEvent.Type.User <= TEST_EVENT_TYPE <= QEvent.Type.MaxUser)
if __name__ == '__main__':
# Copyright (C) 2022 The Qt Company Ltd.
# Copyright (C) 2011 Thomas Perl <thp.io/about>
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
# Test case for PySide bug 829
del confFile
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
gc.collect()
- s = QSettings(self._confFileName, QSettings.IniFormat)
- self.assertEqual(s.status(), QSettings.NoError)
+ s = QSettings(self._confFileName, QSettings.Format.IniFormat)
+ self.assertEqual(s.status(), QSettings.Status.NoError)
# Save value
s.setValue('x', {1: 'a'})
s.sync()
- self.assertEqual(s.status(), QSettings.NoError)
+ self.assertEqual(s.status(), QSettings.Status.NoError)
del s
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
gc.collect()
# Restore value
- s = QSettings(self._confFileName, QSettings.IniFormat)
- self.assertEqual(s.status(), QSettings.NoError)
+ s = QSettings(self._confFileName, QSettings.Format.IniFormat)
+ self.assertEqual(s.status(), QSettings.Status.NoError)
self.assertEqual(s.value('x'), {1: 'a'})
def __del__(self):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
set_counter = 0
-class Descriptor(object):
+class Descriptor:
def __get__(self, obj, owner):
global get_counter
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
def testIt(self):
b = QBuffer()
- b.open(QBuffer.WriteOnly)
+ b.open(QBuffer.OpenModeFlag.WriteOnly)
b.write(bytes("\x0023\x005", "UTF-8"))
b.close()
self.assertEqual(b.buffer().size(), 5)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
def testIt(self):
device = MyIODevice()
- device.open(QIODevice.ReadOnly)
+ device.open(QIODevice.OpenModeFlag.ReadOnly)
s = QTextStream(device)
self.assertEqual(s.read(4), "\0a\0a")
self.assertEqual(device.readLine(), "\0b\0b\0b\0b")
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
eventloop = QEventLoop()
emitter = Emitter()
receiver = Receiver(eventloop)
- emitter.signal.connect(receiver.receive, Qt.BlockingQueuedConnection)
+ emitter.signal.connect(receiver.receive, Qt.ConnectionType.BlockingQueuedConnection)
emitter.start()
retval = eventloop.exec()
emitter.wait(2000)
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for QObject.childEvent and QTimer.childEvent overloading'''
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, QTimer, QCoreApplication
+from PySide6.QtCore import QObject, QTimer
from helper.usesqapplication import UsesQApplication
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
-import sys
import os
import sys
import unittest
pass
self.assertRaises(TypeError, make_info(), test_function)
- class NotAQObject(object):
+ class NotAQObject:
pass
self.assertRaises(TypeError, make_info(), NotAQObject)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
from copy import deepcopy
import os
class QDateTimeDeepCopy(DeepCopyHelper, unittest.TestCase):
def setUp(self):
- self.original = QDateTime(2010, 5, 18, 10, 24, 45, 223, Qt.LocalTime)
+ self.original = QDateTime(2010, 5, 18, 10, 24, 45, 223, Qt.TimeSpec.LocalTime)
class QSizeDeepCopy(DeepCopyHelper, unittest.TestCase):
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QTimer.singleShot'''
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, QTimer, QCoreApplication, SIGNAL
+from PySide6.QtCore import QObject, QTimer
from helper.usesqapplication import UsesQApplication
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
if __name__ == '__main__':
unittest.main()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for duck punching new implementations of C++ virtual methods into object instances.'''
class TestDuckPunchingOnQObjectInstance(UsesQApplication):
- '''Test case for duck punching new implementations of C++ virtual methods into object instances.'''
+ '''Test case for duck punching new implementations of C++ virtual methods into
+ object instances.'''
def setUp(self):
# Acquire resources
parent.childEvent = None
def testChildEventMonkeyPatchWithInheritance(self):
- # Test if the new childEvent injected on a QObject's extension class instance is called from C++
+ '''Test if the new childEvent injected on a QObject's extension class
+ instance is called from C++'''
parent = Duck()
def childEvent(obj, event):
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
emoji-string-test.py
self.testsig.emit(emoji_str)
def plausi(self):
- # Python 2 may be built with UCS-2 or UCS-4 support.
- # UCS-2 creates 2 surrogate code points. See
- # https://stackoverflow.com/questions/30775689/python-length-of-unicode-string-confusion
- assert len(emoji_str) == 2 if sys.maxunicode > 0xffff else 3
+ assert len(emoji_str) == 2
if __name__ == "__main__":
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import os
import sys
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
from PySide6 import QtCore
is_pypy = hasattr(sys, "pypy_version_info")
if not is_pypy:
- from PySide6.support import feature
+ from PySide6.support import feature # noqa: E402
import inspect
probe = "called with wrong argument types"
def setUp(self):
- qApp or QApplication()
+ qApp or QApplication() # noqa
feature.reset()
def tearDown(self):
feature.reset()
- qApp.shutdown()
+ qApp.shutdown() # noqa
def testCorrectErrorMessagesPlain(self):
with self.assertRaises(TypeError) as cm:
self.assertTrue(self.probe in cm.exception.args[0])
def testCorrectErrorMessagesSnake(self):
- from __feature__ import snake_case
+ from __feature__ import snake_case # noqa
with self.assertRaises(TypeError) as cm:
QLabel().set_font(42)
print("\n\n" + cm.exception.args[0])
self.assertTrue(self.probe in cm.exception.args[0])
def testCorrectErrorMessagesProp(self):
- from __feature__ import true_property
+ from __feature__ import true_property # noqa
with self.assertRaises(TypeError) as cm:
QLabel().font = 42
print("\n\n" + cm.exception.args[0])
self.assertTrue(self.probe in cm.exception.args[0])
def testCorrectErrorMessagesSnakeProp(self):
- from __feature__ import snake_case, true_property
+ from __feature__ import snake_case, true_property # noqa
with self.assertRaises(TypeError) as cm:
QLabel().font = 42
print("\n\n" + cm.exception.args[0])
self.assertTrue(self.probe in cm.exception.args[0])
def testCorrectErrorMessagesClassProp(self):
- from __feature__ import true_property
+ from __feature__ import true_property # noqa
with self.assertRaises(TypeError) as cm:
QApplication.quitOnLastWindowClosed = object
print("\n\n" + cm.exception.args[0])
self.assertTrue(self.probe in cm.exception.args[0])
with self.assertRaises(TypeError) as cm:
- qApp.quitOnLastWindowClosed = object
+ qApp.quitOnLastWindowClosed = object # noqa
self.assertTrue(self.probe in cm.exception.args[0])
def testCorrectErrorMessagesClassSnakeProp(self):
- from __feature__ import snake_case, true_property
+ from __feature__ import snake_case, true_property # noqa
with self.assertRaises(TypeError) as cm:
- QApplication.quit_on_last_window_closed = object
+ QApplication.quit_on_last_window_closed = object # noqa
print("\n\n" + cm.exception.args[0])
self.assertTrue(self.probe in cm.exception.args[0])
with self.assertRaises(TypeError) as cm:
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
self.pushButton.setText(QCoreApplication.translate("MainWindow", u"PushButton", None))
# retranslateUi
-
<x>0</x>
<y>0</y>
<width>263</width>
- <height>23</height>
+ <height>26</height>
</rect>
</property>
</widget>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+from __future__ import annotations
"""
feature_with_uic_test.py
from helper.usesqapplication import UsesQApplication
-from PySide6.QtCore import QCoreApplication, QLibraryInfo, qVersion
-from PySide6.QtWidgets import QApplication, QMainWindow
+from PySide6.QtCore import QCoreApplication, qVersion
+from PySide6.QtWidgets import QMainWindow
# PYSIDE-535: We cannot use __feature__ in PyPy, yet
try:
- from __feature__ import snake_case
+ from __feature__ import snake_case # noqa
from feature_with_uic.window import Ui_MainWindow
have_feature = True
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
"""Tests for category logging macros qCDebug, qCInfo, qCWarning, qCCritical"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''(Very) Simple test case for missing names from QtCore'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
# Test case for PySide bug 634
# http://bugs.pyside.org/show_bug.cgi?id=634
from PySide6.QtCore import QCoreApplication
-class Mock(object):
+class Mock:
def __init__(self):
self.called = False
self.return_value = None
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QLineF'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
pass
+class MyMultiDataModel(QAbstractListModel):
+
+ def multiData(self, index, roleSpans):
+ if index.isValid():
+ for rd in roleSpans:
+ if rd.role() == Qt.ItemDataRole.DisplayRole:
+ rd.setData(f"test {index.row()} {index.column()}")
+
+
class TestQModelIndexInternalPointer(unittest.TestCase):
def testInternalPointer(self):
m = MyModel()
foo = QObject()
idx = m.createIndex(0, 0, foo)
- check = m.checkIndex(idx, QAbstractItemModel.CheckIndexOption.IndexIsValid
- | QAbstractItemModel.CheckIndexOption.DoNotUseParent
- | QAbstractItemModel.CheckIndexOption.ParentIsInvalid)
+ flags = (QAbstractItemModel.CheckIndexOption.IndexIsValid
+ | QAbstractItemModel.CheckIndexOption.DoNotUseParent
+ | QAbstractItemModel.CheckIndexOption.ParentIsInvalid)
+ check = m.checkIndex(idx, flags)
self.assertTrue(check)
def testPassQPersistentModelIndexAsQModelIndex(self):
def testQIdentityProxyModel(self):
sourceModel = QStringListModel(['item1', 'item2'])
sourceIndex = sourceModel.index(0, 0)
- sourceData = str(sourceModel.data(sourceIndex, Qt.DisplayRole))
+ sourceData = str(sourceModel.data(sourceIndex, Qt.ItemDataRole.DisplayRole))
proxyModel = QIdentityProxyModel()
proxyModel.setSourceModel(sourceModel)
proxyIndex = proxyModel.mapFromSource(sourceIndex)
- proxyData = str(proxyModel.data(proxyIndex, Qt.DisplayRole))
+ proxyData = str(proxyModel.data(proxyIndex, Qt.ItemDataRole.DisplayRole))
self.assertEqual(sourceData, proxyData)
+ def testMultiDataModel(self):
+ """Test whether QAbstractItemModel.multiData() can be implemented
+ using QModelRoleData/QModelRoleDataSpan (ATM syntax only)."""
+ model = MyMultiDataModel() # noqa: F841
+
if __name__ == '__main__':
unittest.main()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests if QBitArray class is iterable and also '~' (__invert__) and bitwise operators'''
# Tests if QBitArray class is iterable
qbitarray_is_iterable = True
try:
- bitarray = [bit for bit in self.qbitarray]
- except:
+ bitarray = [bit for bit in self.qbitarray] # noqa F841
+ except: # noqa: E722
qbitarray_is_iterable = False
self.assertTrue(qbitarray_is_iterable)
ored_qbitarray, ored_bool_list = None, None
try:
ored_qbitarray = self.qbitarray | self.other_qbitarray
- ored_bool_list = [b1 | b2 for b1, b2 in zip(self.qbitarray_values, self.other_qbitarray_values)]
- except:
+ ored_bool_list = [b1 | b2 for b1, b2 in zip(self.qbitarray_values, self.other_qbitarray_values)] # noqa: E501
+ except: # noqa: E722
has_or_bitwise_operator = False
self.assertTrue(has_or_bitwise_operator)
self.assertEqual(bool_list_from_qbitarray(ored_qbitarray), ored_bool_list)
anded_qbitarray, anded_bool_list = None, None
try:
anded_qbitarray = self.qbitarray | self.other_qbitarray
- anded_bool_list = [b1 | b2 for b1, b2 in zip(self.qbitarray_values, self.other_qbitarray_values)]
- except:
+ anded_bool_list = [b1 | b2 for b1, b2 in zip(self.qbitarray_values, self.other_qbitarray_values)] # noqa: E501
+ except: # noqa: E722
has_and_bitwise_operator = False
self.assertTrue(has_and_bitwise_operator)
self.assertEqual(bool_list_from_qbitarray(anded_qbitarray), anded_bool_list)
xored_qbitarray, xored_bool_list = None, None
try:
xored_qbitarray = self.qbitarray | self.other_qbitarray
- xored_bool_list = [b1 | b2 for b1, b2 in zip(self.qbitarray_values, self.other_qbitarray_values)]
- except:
+ xored_bool_list = [b1 | b2 for b1, b2 in zip(self.qbitarray_values, self.other_qbitarray_values)] # noqa: E501
+ except: # noqa: E722
has_xor_bitwise_operator = False
self.assertTrue(has_xor_bitwise_operator)
self.assertEqual(bool_list_from_qbitarray(xored_qbitarray), xored_bool_list)
if __name__ == '__main__':
unittest.main()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QByteArray concatenation with '+' operator'''
def testConcatPythonStringAndQByteArray(self):
# Test concatenation of a Python bytes with a QByteArray, in this order
- concat_python_string_add_qbytearray_worked = True
qba = QByteArray(bytes('foo', "UTF-8"))
result = bytes('bar\x00', "UTF-8") + qba
self.assertEqual(type(result), QByteArray)
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from helper.docmodifier import DocModifier
-class BaseQByteArrayOperatorIAdd(object):
+class BaseQByteArrayOperatorIAdd:
'''Base class for QByteArray += operator tests.
Implementing classes should inherit from unittest.TestCase and implement
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QByteArray operators'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QByteArray'''
'''
def testIt(self):
a = QByteArray()
- b = QDataStream(a, QIODevice.WriteOnly)
+ b = QDataStream(a, QIODevice.OpenModeFlag.WriteOnly)
b.writeUInt16(5000)
# The __repr__ not suppose to crash anymore
self.assertNotEqual(repr(b), None)
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QCalendar (5.14)'''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QCbor'''
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import (QByteArray, QCborStreamReader, QCborStreamWriter,
- QCborTag, QCborValue)
+from PySide6.QtCore import QByteArray, QCborStreamReader, QCborStreamWriter, QCborValue
class TestCbor(unittest.TestCase):
value = reader.toInteger()
self.assertEqual(value, 42)
- def testReader(self):
+ def anotherTestReader(self):
ba = QByteArray()
writer = QCborStreamWriter(ba)
writer.append("hello")
self.assertTrue(not ba.isEmpty())
reader = QCborStreamReader(ba)
self.assertTrue(reader.hasNext())
- if (reader.isByteArray()): # Python 2
- value = reader.readByteArray()
- self.assertTrue(value)
- self.assertEqual(value.data, "hello")
- else:
- self.assertTrue(reader.isString())
- value = reader.readString()
- self.assertTrue(value)
- self.assertEqual(value.data, "hello")
+ self.assertTrue(reader.isString())
+ value = reader.readString()
+ self.assertTrue(value)
+ self.assertEqual(value.data, "hello")
def testValue(self):
value = QCborValue('hello')
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QCollator'''
-import ctypes
import os
import sys
import unittest
class QCollatorTest(unittest.TestCase):
def testState(self):
c = QCollator()
- c.setCaseSensitivity(Qt.CaseInsensitive)
- c.setLocale(QLocale.German)
+ c.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
+ c.setLocale(QLocale.Language.German)
print("compare a and b:", c.compare("a", "b"))
- self.assertEqual(c.caseSensitivity(), Qt.CaseInsensitive)
- self.assertEqual(c.locale(), QLocale(QLocale.German))
+ self.assertEqual(c.caseSensitivity(), Qt.CaseSensitivity.CaseInsensitive)
+ self.assertEqual(c.locale(), QLocale(QLocale.Language.German))
- c.setLocale(QLocale.French)
+ c.setLocale(QLocale.Language.French)
c.setNumericMode(True)
c.setIgnorePunctuation(True)
- c.setLocale(QLocale.NorwegianBokmal)
+ c.setLocale(QLocale.Language.NorwegianBokmal)
- self.assertEqual(c.caseSensitivity(), Qt.CaseInsensitive)
+ self.assertEqual(c.caseSensitivity(), Qt.CaseSensitivity.CaseInsensitive)
self.assertEqual(c.numericMode(), True)
self.assertEqual(c.ignorePunctuation(), True)
- self.assertEqual(c.locale(), QLocale(QLocale.NorwegianBokmal))
+ self.assertEqual(c.locale(), QLocale(QLocale.Language.NorwegianBokmal))
if __name__ == '__main__':
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QCommandLineParser and QCommandLineOption'''
-import ctypes
import os
import sys
import unittest
class QCommandLineParserTest(unittest.TestCase):
def testParser(self):
- app = QCoreApplication([])
+ app = QCoreApplication([]) # noqa
parser1 = QCommandLineParser()
self.assertEqual(parser1.parse(["QtCore_qcommandlineparser_test", "file.txt"]), True)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QCoreApplication.instance static method'''
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QDataStream'''
def serialize_bitarray(bit_array):
buffer = QByteArray()
- stream = QDataStream(buffer, QIODevice.WriteOnly)
+ stream = QDataStream(buffer, QIODevice.OpenModeFlag.WriteOnly)
stream << bit_array
return buffer
def setUp(self):
self.ba = QByteArray()
- self.read = QDataStream(self.ba, QIODevice.ReadOnly)
- self.write = QDataStream(self.ba, QIODevice.WriteOnly)
+ self.read = QDataStream(self.ba, QIODevice.OpenModeFlag.ReadOnly)
+ self.write = QDataStream(self.ba, QIODevice.OpenModeFlag.WriteOnly)
def testWriteUInt8(self):
'''QDataStream.writeUInt8 (accepting str of size 1)'''
def setUp(self):
self.ba = QByteArray()
- self.stream = QDataStream(self.ba, QIODevice.WriteOnly)
- self.read_stream = QDataStream(self.ba, QIODevice.ReadOnly)
+ self.stream = QDataStream(self.ba, QIODevice.OpenModeFlag.WriteOnly)
+ self.read_stream = QDataStream(self.ba, QIODevice.OpenModeFlag.ReadOnly)
def testQCharValid(self):
'''QDataStream <<>> QChar - valid'''
'''Check the >> operator for the given data set'''
for data, expectedStatus, expectedString in data_set:
- stream = QDataStream(data, QIODevice.ReadOnly)
+ stream = QDataStream(data, QIODevice.OpenModeFlag.ReadOnly)
string = QBitArray()
stream >> string
data = []
for expected in test_set:
- data.append((serialize_bitarray(expected), QDataStream.Ok, expected))
+ data.append((serialize_bitarray(expected), QDataStream.Status.Ok, expected))
self._check_bitarray(data)
def testPastEnd(self):
'''QDataStream >> QBitArray reading past the end of the data'''
serialized = serialize_bitarray(create_bitarray('1001110'))
serialized.resize(serialized.size() - 2)
- self._check_bitarray([(serialized, QDataStream.ReadPastEnd, QBitArray())])
+ self._check_bitarray([(serialized, QDataStream.Status.ReadPastEnd, QBitArray())])
class QDataStreamBuffer(unittest.TestCase):
self.assertEqual(data.readRawData(4), None)
ba = QByteArray()
- data = QDataStream(ba, QIODevice.WriteOnly)
+ data = QDataStream(ba, QIODevice.OpenModeFlag.WriteOnly)
data.writeRawData('AB\x00C')
self.assertEqual(ba.data(), bytes('AB\x00C', "UTF-8"))
test_data = b'AB\0'
data = QDataStream()
ba = QByteArray()
- data = QDataStream(ba, QIODevice.WriteOnly)
+ data = QDataStream(ba, QIODevice.OpenModeFlag.WriteOnly)
data.writeRawData(test_data)
self.assertEqual(ba.data(), test_data)
data = QDataStream(ba)
self.assertEqual(dataOne.readBytes(4), None)
ba = QByteArray()
- data = QDataStream(ba, QIODevice.WriteOnly)
+ data = QDataStream(ba, QIODevice.OpenModeFlag.WriteOnly)
# writeBytes() writes a quint32 containing the length of the data,
# followed by the data.
data.writeBytes(bytes('AB\x00C', 'UTF-8'))
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QDate'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import datetime
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QDir
+from PySide6.QtCore import QDir, QDirListing
class QDirTest(unittest.TestCase):
self.assertEqual(b, c)
+class QDirListingTest(unittest.TestCase):
+ '''Test case for QDirListing'''
+
+ def test(self):
+ dir_listing = QDirListing(QDir.homePath())
+ for entry in dir_listing:
+ pass
+
+
if __name__ == '__main__':
unittest.main()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QEnum and QFlags'''
def testEnumNew_NoLeak(self):
gc.collect()
total = sys.gettotalrefcount()
- for idx in range(1000):
- ret = Qt.Key(42)
+ for _ in range(1000):
+ ret = Qt.Key(42) # noqa: F841
gc.collect()
delta = sys.gettotalrefcount() - total
class TestQFlags(unittest.TestCase):
def testToItn(self):
- om = QIODevice.NotOpen
+ om = QIODevice.OpenModeFlag.NotOpen
omcmp = om.value
- self.assertEqual(om, QIODevice.NotOpen)
+ self.assertEqual(om, QIODevice.OpenModeFlag.NotOpen)
self.assertTrue(omcmp == 0)
- self.assertTrue(omcmp != QIODevice.ReadOnly)
+ self.assertTrue(omcmp != QIODevice.OpenModeFlag.ReadOnly)
self.assertTrue(omcmp != 1)
def testToIntInFunction(self):
- om = QIODevice.WriteOnly
+ om = QIODevice.OpenModeFlag.WriteOnly
self.assertEqual(int(om.value), 2)
def testNonExtensibleEnums(self):
try:
- om = QIODevice.OpenMode(QIODevice.WriteOnly)
+ om = QIODevice.OpenMode(QIODevice.OpenModeFlag.WriteOnly) # noqa: F841
self.assertFail()
- except:
+ except: # noqa: E722
pass
def testPickleEnum(self):
# Pickling of enums with different depth works.
- ret = pickle.loads(pickle.dumps(QIODevice.Append))
- self.assertEqual(ret, QIODevice.Append)
+ ret = pickle.loads(pickle.dumps(QIODevice.OpenModeFlag.Append))
+ self.assertEqual(ret, QIODevice.OpenModeFlag.Append)
ret = pickle.loads(pickle.dumps(Qt.Key.Key_Asterisk))
self.assertEqual(ret, Qt.Key.Key_Asterisk)
# This works also with nested classes for Python 3, after we
# introduced the correct __qualname__ attribute.
-
- # Note: For Python 2, we would need quite strange patches.
- func = lambda: pickle.loads(pickle.dumps(Qt.Key))
+ func = lambda: pickle.loads(pickle.dumps(Qt.Key)) # noqa: E731
func()
# PYSIDE-957: The QEnum macro
HAVE_ENUM = True
except ImportError:
HAVE_ENUM = False
- QEnum = QFlag = lambda x: x
+ QEnum = QFlag = lambda x: x # noqa: F811
import types
class Enum:
class InnerEnum(enum.Enum):
X = 42
- class SomeEnum(enum.Enum):
+ class SomeEnum(enum.Enum): # noqa: F811
A = 4
B = 5
C = 6
int(SomeClass.SomeEnum.C) == 6
self.assertEqual(SomeClass.OtherEnum.C, 3)
- @unittest.skipIf(sys.version_info[0] < 3, "we cannot support nested classes in Python 2")
def testInnerClass(self):
self.assertEqual(SomeClass.InnerClass.InnerEnum.__qualname__,
- "SomeClass.InnerClass.InnerEnum")
+ "SomeClass.InnerClass.InnerEnum")
with self.assertRaises(TypeError):
int(SomeClass.InnerClass.InnerEnum.X) == 42
self.assertEqual(mo.enumerator(1).name(), "SomeEnum")
moi = SomeClass.InnerClass.staticMetaObject
self.assertEqual(moi.enumerator(0).name(), "InnerEnum")
- ## Question: Should that scope not better be "SomeClass.InnerClass"?
- ## But we have __qualname__ already:
+ # Question: Should that scope not better be "SomeClass.InnerClass"?
+ # But we have __qualname__ already:
self.assertEqual(moi.enumerator(0).scope(), "InnerClass")
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QtCore.QEvent'''
def testFlagAccess(self):
# QEvent.Type flags usage
- event = QEvent(QEvent.Timer)
- self.assertEqual(event.type(), QEvent.Timer)
+ event = QEvent(QEvent.Type.Timer)
+ self.assertEqual(event.type(), QEvent.Type.Timer)
- event = QEvent(QEvent.Close)
- self.assertEqual(event.type(), QEvent.Close)
+ event = QEvent(QEvent.Type.Close)
+ self.assertEqual(event.type(), QEvent.Type.Close)
- event = QEvent(QEvent.IconTextChange)
- self.assertEqual(event.type(), QEvent.IconTextChange)
+ event = QEvent(QEvent.Type.IconTextChange)
+ self.assertEqual(event.type(), QEvent.Type.IconTextChange)
if __name__ == '__main__':
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
def testBasic(self):
'''QFile.getChar'''
obj = QFile(self.filename)
- obj.open(QIODevice.ReadOnly)
+ obj.open(QIODevice.OpenModeFlag.ReadOnly)
try:
self.assertEqual(obj.getChar(), (True, 'a'))
self.assertFalse(obj.getChar()[0])
def testBug721(self):
obj = QFile(self.filename)
- obj.open(QIODevice.ReadOnly)
+ obj.open(QIODevice.OpenModeFlag.ReadOnly)
try:
memory = obj.map(0, 1)
self.assertEqual(len(memory), 1)
dir = QTemporaryDir(QDir.tempPath() + "/XXXXXX.dir")
self.assertTrue(dir.isValid())
saveFile = QSaveFile(dir.path() + "/test.dat")
- self.assertTrue(saveFile.open(QIODevice.WriteOnly))
+ self.assertTrue(saveFile.open(QIODevice.OpenModeFlag.WriteOnly))
saveFile.write(bytes("Test", "UTF-8"))
self.assertTrue(saveFile.commit())
self.assertTrue(os.path.exists(QDir.toNativeSeparators(saveFile.fileName())))
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
-import tempfile
import unittest
from pathlib import Path
def testBasic(self):
'''QFileInfo(QFile)'''
- obj = QFileInfo(QFile())
+ obj = QFileInfo(QFile()) # noqa
def testQFileInfoPath(self):
# PYSIDE-1499: Test QFileInfo with Path objects
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QIODevice, QTemporaryFile
+from PySide6.QtCore import QTemporaryFile
class FileChild1(QTemporaryFile):
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QFlags'''
-import operator
import os
import sys
import unittest
f.close()
f = QFile(fileName)
- self.assertEqual(f.open(QIODevice.Truncate | QIODevice.Text | QIODevice.ReadWrite), True)
+ of = (QIODevice.OpenModeFlag.Truncate | QIODevice.OpenModeFlag.Text
+ | QIODevice.OpenModeFlag.ReadWrite)
+ self.assertEqual(f.open(of), True)
om = f.openMode()
- self.assertEqual(om & QIODevice.Truncate, QIODevice.Truncate)
- self.assertEqual(om & QIODevice.Text, QIODevice.Text)
- self.assertEqual(om & QIODevice.ReadWrite, QIODevice.ReadWrite)
- self.assertTrue(om == QIODevice.Truncate | QIODevice.Text | QIODevice.ReadWrite)
+ self.assertEqual(om & QIODevice.OpenModeFlag.Truncate, QIODevice.OpenModeFlag.Truncate)
+ self.assertEqual(om & QIODevice.OpenModeFlag.Text, QIODevice.OpenModeFlag.Text)
+ self.assertEqual(om & QIODevice.OpenModeFlag.ReadWrite, QIODevice.OpenModeFlag.ReadWrite)
+ expected = (QIODevice.OpenModeFlag.Truncate | QIODevice.OpenModeFlag.Text
+ | QIODevice.OpenModeFlag.ReadWrite)
+ self.assertTrue(om == expected)
f.close()
def testInvert(self):
'''QFlags ~ (invert) operator'''
- self.assertEqual(type(~QIODevice.ReadOnly), QIODevice.OpenMode)
+ self.assertEqual(type(~QIODevice.OpenModeFlag.ReadOnly), QIODevice.OpenMode)
def testOr(self):
'''QFlags | (or) operator'''
- self.assertEqual(type(QIODevice.ReadOnly | QIODevice.WriteOnly), QIODevice.OpenMode)
+ self.assertEqual(type(QIODevice.OpenModeFlag.ReadOnly | QIODevice.OpenModeFlag.WriteOnly),
+ QIODevice.OpenMode)
def testAnd(self):
'''QFlags & (and) operator'''
- self.assertEqual(type(QIODevice.ReadOnly & QIODevice.WriteOnly), QIODevice.OpenMode)
+ self.assertEqual(type(QIODevice.OpenModeFlag.ReadOnly & QIODevice.OpenModeFlag.WriteOnly),
+ QIODevice.OpenMode)
def testIOr(self):
'''QFlags |= (ior) operator'''
flag = Qt.WindowFlags()
- self.assertTrue(Qt.Widget == 0)
- self.assertFalse(flag & Qt.Widget)
- result = flag & Qt.Widget
+ self.assertTrue(Qt.WindowType.Widget == 0)
+ self.assertFalse(flag & Qt.WindowType.Widget)
+ result = flag & Qt.WindowType.Widget
self.assertTrue(result == 0)
- flag |= Qt.WindowMinimizeButtonHint
- self.assertTrue(flag & Qt.WindowMinimizeButtonHint)
+ flag |= Qt.WindowType.WindowMinimizeButtonHint
+ self.assertTrue(flag & Qt.WindowType.WindowMinimizeButtonHint)
def testInvertOr(self):
'''QFlags ~ (invert) operator over the result of an | (or) operator'''
- self.assertEqual(type(~(Qt.ItemIsSelectable | Qt.ItemIsEditable)), Qt.ItemFlags)
+ self.assertEqual(type(~(Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEditable)),
+ Qt.ItemFlags)
def testEqual(self):
'''QFlags == operator'''
- flags = Qt.Window
- flags |= Qt.WindowMinimizeButtonHint
- flag_type = (flags & Qt.WindowType_Mask)
- self.assertEqual(flag_type, Qt.Window)
+ flags = Qt.WindowType.Window
+ flags |= Qt.WindowType.WindowMinimizeButtonHint
+ flag_type = (flags & Qt.WindowType.WindowType_Mask)
+ self.assertEqual(flag_type, Qt.WindowType.Window)
- self.assertEqual(Qt.KeyboardModifiers(Qt.ControlModifier), Qt.ControlModifier)
+ self.assertEqual(Qt.KeyboardModifiers(Qt.KeyboardModifier.ControlModifier),
+ Qt.KeyboardModifier.ControlModifier)
def testOperatorBetweenFlags(self):
'''QFlags & QFlags'''
- flags = Qt.NoItemFlags | Qt.ItemIsUserCheckable
- newflags = Qt.NoItemFlags | Qt.ItemIsUserCheckable
+ flags = Qt.ItemFlag.NoItemFlags | Qt.ItemFlag.ItemIsUserCheckable
+ newflags = Qt.ItemFlag.NoItemFlags | Qt.ItemFlag.ItemIsUserCheckable
self.assertTrue(flags & newflags)
def testOperatorDifferentOrder(self):
'''Different ordering of arguments'''
- flags = Qt.NoItemFlags | Qt.ItemIsUserCheckable
- self.assertEqual(flags | Qt.ItemIsEnabled, Qt.ItemIsEnabled | flags)
+ flags = Qt.ItemFlag.NoItemFlags | Qt.ItemFlag.ItemIsUserCheckable
+ self.assertEqual(flags | Qt.ItemFlag.ItemIsEnabled, Qt.ItemFlag.ItemIsEnabled | flags)
def testEqualNonNumericalObject(self):
'''QFlags ==,!= non-numerical object '''
- flags = Qt.NoItemFlags | Qt.ItemIsUserCheckable
+ flags = Qt.ItemFlag.NoItemFlags | Qt.ItemFlag.ItemIsUserCheckable
self.assertTrue(flags != None) # noqa: E711
self.assertFalse(flags == None) # noqa: E711
class QFlagsOnQVariant(unittest.TestCase):
def testQFlagsOnQVariant(self):
o = QObject()
- o.setProperty("foo", QIODevice.ReadOnly | QIODevice.WriteOnly)
+ o.setProperty("foo", QIODevice.OpenModeFlag.ReadOnly | QIODevice.OpenModeFlag.WriteOnly)
self.assertEqual(type(o.property("foo")), QIODevice.OpenMode)
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QEnum and QFlags'''
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QThread, Qt
+from PySide6.QtCore import QThread
class TestHANDLE(unittest.TestCase):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for qInstallMsgHandler'''
ret = qInstallMessageHandler(None)
self.assertEqual(ret, None)
refcount = sys.getrefcount(handleruseless)
- retNone = qInstallMessageHandler(handleruseless)
+ retNone = qInstallMessageHandler(handleruseless) # noqa: F841
self.assertEqual(sys.getrefcount(handleruseless), refcount + 1)
rethandler = qInstallMessageHandler(None)
self.assertEqual(rethandler, handleruseless)
self.assertEqual(sys.getrefcount(handleruseless), refcount)
def testHandler(self):
- rethandler = qInstallMessageHandler(handler)
+ rethandler = qInstallMessageHandler(handler) # noqa: F841
if QLibraryInfo.isDebugBuild():
qDebug("Test Debug")
self.assertEqual(param[0], QtMsgType.QtDebugMsg)
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for buffered read methods of QIODevice'''
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for the QIOPipe class'''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QJsonDocument/nullptr_t'''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QLineF'''
tuple_ = l1.intersects(l2)
self.assertEqual(tuple, tuple_.__class__)
(value, p) = tuple_
- self.assertEqual(QLineF.BoundedIntersection, value)
+ self.assertEqual(QLineF.IntersectionType.BoundedIntersection, value)
self.assertEqual(QPointF(1, 0), p)
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QLocale'''
class QLocaleTestToNumber(unittest.TestCase):
def testToNumberInt(self):
- obj = QLocale(QLocale.C)
+ obj = QLocale(QLocale.Language.C)
self.assertEqual((37, True), obj.toInt('37'))
def testToNumberFloat(self):
- obj = QLocale(QLocale.C)
+ obj = QLocale(QLocale.Language.C)
self.assertEqual((ctypes.c_float(37.109).value, True),
obj.toFloat('37.109'))
def testToNumberDouble(self):
- obj = QLocale(QLocale.C)
+ obj = QLocale(QLocale.Language.C)
self.assertEqual((ctypes.c_double(37.109).value, True),
obj.toDouble('37.109'))
def testToNumberShort(self):
- obj = QLocale(QLocale.C)
+ obj = QLocale(QLocale.Language.C)
self.assertEqual((ctypes.c_short(37).value, True),
obj.toShort('37'))
def testToNumberLong(self):
- obj = QLocale(QLocale.C)
+ obj = QLocale(QLocale.Language.C)
self.assertEqual((ctypes.c_long(37).value, True),
obj.toLong('37'))
def testToNumberULongLong(self):
- obj = QLocale(QLocale.C)
+ obj = QLocale(QLocale.Language.C)
self.assertEqual((ctypes.c_ulonglong(37).value, True),
obj.toULongLong('37'))
def testToNumberULongLongNegative(self):
- obj = QLocale(QLocale.C)
+ obj = QLocale(QLocale.Language.C)
self.assertTrue(not obj.toULongLong('-37')[1])
def testToCurrencyString(self):
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QLockFile'''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QMessageAuthenticationCode'''
class TestQMessageAuthenticationCode (unittest.TestCase):
def test(self):
- code = QMessageAuthenticationCode(QCryptographicHash.Sha1, bytes('bla', "UTF-8"))
+ code = QMessageAuthenticationCode(QCryptographicHash.Algorithm.Sha1,
+ bytes('bla', "UTF-8"))
result = code.result()
self.assertTrue(result.size() > 0)
print(result.toHex())
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests for static methos conflicts with class methods'''
app = QCoreApplication() # noqa: F841
sender = SemaphoreSender()
receiver = SemaphoreReceiver()
- sender.signal.connect(receiver.receiverSlot, Qt.QueuedConnection)
+ sender.signal.connect(receiver.receiverSlot, Qt.ConnectionType.QueuedConnection)
sender.emitSignal()
while not receiver.semaphore:
QCoreApplication.processEvents()
#!/usr/bin/python
# Copyright (C) 2022The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests for QMetaType'''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QMimeDatabase'''
self.assertTrue(s1.isValid())
self.assertEqual(s1.name(), "text/plain")
- krita = db.mimeTypeForName("application/x-krita")
- self.assertTrue(krita.isValid())
+ # Removed because of the move of to the Tika mimetypes.
+ # krita = db.mimeTypeForName("application/x-krita")
+ # self.assertTrue(krita.isValid())
rdf = db.mimeTypeForName("application/rdf+xml")
self.assertTrue(rdf.isValid())
self.assertEqual(rdf.name(), "application/rdf+xml")
self.assertTrue(rdf.comment())
if "en" in QLocale().name():
- self.assertEqual(rdf.comment(), "RDF file")
+ self.assertTrue(rdf.comment() in ("RDF file", "XML syntax for RDF graphs"))
bzip2 = db.mimeTypeForName("application/x-bzip2")
self.assertTrue(bzip2.isValid())
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test case for QAbstractListModel.createIndex and QModelIndex.internalPointer'''
def testInternalPointer(self):
# Test QAbstractListModel.createIndex and
# QModelIndex.internalPointer with regular Python objects
- obj = QObject()
+ obj = QObject() # noqa: F841
idx = self.model.createIndex(0, 0, "Hello")
i = idx.internalPointer()
self.assertEqual(i, "Hello")
o = [1, 2, 3]
o_refcnt = sys.getrefcount(o)
idx = self.model.createIndex(0, 0, o)
- ptr = idx.internalPointer()
+ ptr = idx.internalPointer() # noqa: F841
self.assertEqual(sys.getrefcount(o), o_refcnt + 1)
def testIndexForDefaultDataArg(self):
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for parent-child relationship'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test case for QObject::connectNotify()'''
receiver = QObject()
sender.connect(SIGNAL("destroyed()"), receiver, SLOT("deleteLater()"))
self.assertTrue(sender.con_notified)
- # When connecting to a regular slot, and not a python callback function, QObject::connect
- # will use the non-cloned method signature, so connecting to destroyed() will actually
- # connect to destroyed(QObject*).
self.assertEqual(sender.signal.methodSignature(), "destroyed(QObject*)")
self.assertTrue(sender.disconnect(SIGNAL("destroyed()"), receiver, SLOT("deleteLater()")))
self.assertTrue(sender.dis_notified)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QObject.eventFilter'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QObject methods'''
# QObject.inherits(classname) should fail if classname isn't a
# QObject subclass
- class Parent(object):
+ class Parent:
# Dummy parent
pass
def testMultipleInheritance(self):
def declareClass():
+ # Note: 'object' cannot removed from this class declaration
+ # in order to make it work with QObject.
class Foo(object, QObject):
pass
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for the bug #378
http://bugs.openbossa.org/show_bug.cgi?id=378
'''Test case for the bug #378'''
def testBug378(self):
- obj = ExtQObject()
+ obj = ExtQObject() # noqa: F841
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for parent-child relationship'''
search_result = parent.findChild(QObject, nested_child_name)
self.assertTrue(search_result)
search_result = parent.findChild(QObject, nested_child_name,
- Qt.FindChildrenRecursively)
+ Qt.FindChildOption.FindChildrenRecursively)
self.assertTrue(search_result)
search_result = parent.findChild(QObject, nested_child_name,
- Qt.FindDirectChildrenOnly)
+ Qt.FindChildOption.FindDirectChildrenOnly)
self.assertFalse(search_result)
search_results = parent.findChildren(QObject, nested_child_name)
self.assertEqual(len(search_results), 1)
search_result = parent.findChildren(QObject, nested_child_name,
- Qt.FindChildrenRecursively)
+ Qt.FindChildOption.FindChildrenRecursively)
self.assertEqual(len(search_results), 1)
search_results = parent.findChildren(QObject, nested_child_name,
- Qt.FindDirectChildrenOnly)
+ Qt.FindChildOption.FindDirectChildrenOnly)
self.assertEqual(len(search_results), 0)
def testFindChildWithoutName(self):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QObject property and setProperty'''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QObject protected methods'''
self.assertEqual(obj.receivers(SIGNAL("destroyed()")), 0)
obj.destroyed.connect(self.cb)
- self.assertEqual(obj.receivers(SIGNAL("destroyed()")), 1)
+ self.assertTrue(obj.receivers(SIGNAL("destroyed()")) > 0)
def testQThreadReceiversExtern(self):
# QThread.receivers() - Inherited protected method
obj = QThread()
- self.assertEqual(obj.receivers(SIGNAL('destroyed()')), 0)
+ old_count = obj.receivers(SIGNAL('destroyed()'))
obj.destroyed.connect(self.cb)
- self.assertEqual(obj.receivers(SIGNAL("destroyed()")), 1)
+ self.assertTrue(obj.receivers(SIGNAL("destroyed()")) > old_count)
if __name__ == '__main__':
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QObject methods'''
obj = Obj()
# On first connect, UniqueConnection returns True, and on the second
# it must return False, and not a RuntimeError (PYSIDE-34)
- self.assertTrue(obj.signal.connect(obj.empty, Qt.UniqueConnection))
- self.assertFalse(obj.signal.connect(obj.empty, Qt.UniqueConnection))
+ self.assertTrue(obj.signal.connect(obj.empty, Qt.ConnectionType.UniqueConnection))
+ self.assertFalse(obj.signal.connect(obj.empty, Qt.ConnectionType.UniqueConnection))
def testDisconnect(self):
obj = Obj()
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for QObject.timerEvent overloading'''
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, QCoreApplication
+from PySide6.QtCore import QObject
from helper.usesqapplication import UsesQApplication
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QObject's tr static methods.'''
import gc
import os
-import os
import sys
import unittest
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QPoint and QPointF'''
class QPointTest(unittest.TestCase):
def testQPointCtor(self):
- point = QPoint(QPoint(10, 20))
+ point = QPoint(QPoint(10, 20)) # noqa: F841
class QPointFTest(unittest.TestCase):
def testQPointFCtor(self):
- pointf = QPointF(QPoint(10, 20))
+ pointf = QPointF(QPoint(10, 20)) # noqa: F841
if __name__ == '__main__':
unittest.main()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QProcess'''
pid = p.processId()
# We can't test the pid method result because it returns 0 when the
# process isn't running
- if p.state() == QProcess.Running:
+ if p.state() == QProcess.ProcessState.Running:
self.assertNotEqual(pid, 0)
p.waitForFinished()
else:
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QRect'''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QRegularExpression'''
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QRegularExpression, QRegularExpressionMatch, QRegularExpressionMatchIterator
+from PySide6.QtCore import QRegularExpression
class QRegularExpressionTest(unittest.TestCase):
self.assertEqual(match.capturedView(1), 'word2')
def testMatchIterator(self):
- re = QRegularExpression('(\w+)')
+ re = QRegularExpression(r'(\w+)')
self.assertTrue(re.isValid())
count = 0
it = re.globalMatch('word1 word2 word3')
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QResource usage'''
init_test_paths(False)
from PySide6.QtCore import QByteArray, QFile, QIODevice
-import resources_mc
+import resources_mc # noqa: F401
class ResourcesUsage(unittest.TestCase):
orig.remove(carriage_return, 1)
f = QFile(':/quote.txt') # |QIODevice.Text
- self.assertTrue(f.open(QIODevice.ReadOnly), f.errorString())
+ self.assertTrue(f.open(QIODevice.OpenModeFlag.ReadOnly), f.errorString())
copy = f.readAll()
f.close()
self.assertEqual(orig, copy)
orig = file.read_bytes()
f = QFile(':/sample.png')
- self.assertTrue(f.open(QIODevice.ReadOnly), f.errorString())
+ self.assertTrue(f.open(QIODevice.OpenModeFlag.ReadOnly), f.errorString())
copy = f.readAll()
f.close()
self.assertEqual(len(orig), len(copy))
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QRunnable'''
import os
import sys
import unittest
-from io import StringIO
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QCoreApplication, QRunnable, QThreadPool, QThread, qDebug
+from PySide6.QtCore import QRunnable, QThreadPool
from helper.usesqapplication import UsesQApplication
test_result = ""
[General]
var1=a, b, c
var2=a
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QDate'''
file = Path(__file__).resolve().parent / 'qsettings_test.ini'
self.assertTrue(file.is_file())
file_path = QDir.fromNativeSeparators(os.fspath(file))
- settings = QSettings(file_path, QSettings.IniFormat)
+ settings = QSettings(file_path, QSettings.Format.IniFormat)
r = settings.value('var1')
self.assertEqual(type(r), list)
dir = QTemporaryDir(f'{temp_dir}/qsettings_XXXXXX')
self.assertTrue(dir.isValid())
file_name = dir.filePath('foo.ini')
- settings = QSettings(file_name, QSettings.IniFormat)
+ settings = QSettings(file_name, QSettings.Format.IniFormat)
sample_list = ["a", "b"]
string_list_of_empty = [""]
settings.setValue('zero_value', 0)
gc.collect()
# Loading values already set
- settings = QSettings(file_name, QSettings.IniFormat)
+ settings = QSettings(file_name, QSettings.Format.IniFormat)
# Getting value that doesn't exist
r = settings.value("variable")
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Client for the unit test of QSharedMemory'''
print('Pass segment name', file=sys.stderr)
sys.exit(-1)
shared_memory = QSharedMemory(sys.argv[1])
- if not shared_memory.attach(QSharedMemory.ReadOnly):
+ name = "I have no name" # Fixme: What should be there?
+ if not shared_memory.attach(QSharedMemory.AccessMode.ReadOnly):
raise SystemError(f'attach to "{name}" failed')
if not shared_memory.lock():
raise SystemError(f'lock of "{name}" failed')
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QSharedMemory'''
-import ctypes
import os
import subprocess
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QSize'''
if __name__ == '__main__':
unittest.main()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QUuid'''
class QSocketNotifierTest(unittest.TestCase):
def testClass(self):
- app = QCoreApplication([])
+ app = QCoreApplication([]) # noqa: F841
# socketpair is not available on Windows
if os.name != "nt":
w_sock, r_sock = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
self.assertIsInstance(r_sock.fileno(), int)
- notifier = QSocketNotifier(r_sock.fileno(), QSocketNotifier.Read)
+ notifier = QSocketNotifier(r_sock.fileno(), QSocketNotifier.Type.Read)
self.assertIsNotNone(notifier)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QStandardPaths'''
-import ctypes
import os
import sys
import unittest
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QStorageInfo'''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QString'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
class TestQSysInfo(unittest.TestCase):
def testEnumEndian(self):
- self.assertEqual(QSysInfo.BigEndian.value, 0)
- self.assertEqual(QSysInfo.LittleEndian.value, 1)
- self.assertTrue(QSysInfo.ByteOrder.value > -1)
+ self.assertEqual(QSysInfo.Endian.BigEndian.value, 0)
+ self.assertEqual(QSysInfo.Endian.LittleEndian.value, 1)
+ self.assertTrue(QSysInfo.Endian.ByteOrder.value > -1)
def testEnumSizes(self):
- self.assertTrue(QSysInfo.WordSize.value > 0)
+ self.assertTrue(QSysInfo.Sizes.WordSize.value > 0)
if __name__ == '__main__':
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QTextStream'''
def setUp(self):
self.ba = QByteArray()
- self.read = QTextStream(self.ba, QIODevice.ReadOnly)
- self.write = QTextStream(self.ba, QIODevice.WriteOnly)
+ self.read = QTextStream(self.ba, QIODevice.OpenModeFlag.ReadOnly)
+ self.write = QTextStream(self.ba, QIODevice.OpenModeFlag.WriteOnly)
def testNumber(self):
'''QTextStream << number'''
data.append((QByteArray(bytes('ole', "UTF-8")), ['ole']))
data.append((QByteArray(bytes('ole\n', "UTF-8")), ['ole']))
data.append((QByteArray(bytes('ole\r\n', "UTF-8")), ['ole']))
- data.append((QByteArray(bytes('ole\r\ndole\r\ndoffen', "UTF-8")), ['ole', 'dole', 'doffen']))
+ data.append((QByteArray(bytes('ole\r\ndole\r\ndoffen', "UTF-8")),
+ ['ole', 'dole', 'doffen']))
self._check_data(data)
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Producer-Consumer test/example with QThread'''
logging.basicConfig(level=logging.WARNING)
-from PySide6.QtCore import QThread, QCoreApplication, QObject, SIGNAL
+from PySide6.QtCore import QThread, QCoreApplication, QObject
class Bucket(QObject):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for connecting signals between threads'''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QThread'''
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QThread, QCoreApplication, QObject, QTimer, Slot
-from PySide6.QtCore import QEventLoop
+from PySide6.QtCore import QThread, QObject, QTimer, Slot
from helper.usesqapplication import UsesQApplication
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QTimer.singleShot'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for timeout() signals from QTimer object.'''
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, QTimer, SIGNAL
+from PySide6.QtCore import QObject, QTimer
from helper.usesqapplication import UsesQApplication
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
timeZone = QTimeZone(id)
self.assertTrue(timeZone.isValid())
self.assertEqual(timeZone.id(), id)
- name = timeZone.displayName(QTimeZone.GenericTime, QTimeZone.DefaultName)
+ name = timeZone.displayName(QTimeZone.TimeType.GenericTime,
+ QTimeZone.NameType.DefaultName)
self.assertTrue(name)
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test suite for QtCore.Qt namespace'''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test suite for QtCore.QUrl'''
url.setPath('/mail/view')
self.assertEqual(url.toString(),
- 'ftp://www.google.com:8080/mail/view')
+ 'ftp://www.google.com:8080/mail/view')
url.setUserName('john')
self.assertEqual(url.toString(),
- 'ftp://john@www.google.com:8080/mail/view')
+ 'ftp://john@www.google.com:8080/mail/view')
url.setPassword('abc123')
self.assertEqual(url.toString(),
- 'ftp://john:abc123@www.google.com:8080/mail/view')
+ 'ftp://john:abc123@www.google.com:8080/mail/view')
class QueryItemsTest(unittest.TestCase):
for i, data in enumerate(valid_data):
url.addQueryItem(key, data)
self.assertEqual(url.allQueryItemValues(key),
- list(valid_data[:i + 1]))
+ list(valid_data[:i + 1]))
def testPath(self):
url = QUrl("http://qt-project.org/images/ban/pgs_front.jpg")
# PYSIDE-345: No bindings for QUrlQuery
-class QueryItemsTest(unittest.TestCase):
+class QueryItemsTest2(unittest.TestCase):
'''Test query item management'''
def testQueryItems(self):
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QUrlQuery'''
-import ctypes
import os
import sys
import unittest
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QUuid'''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QVersionNumber'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from init_paths import init_test_paths
init_test_paths(False)
-import PySide6
+# for 'self.original'
+import PySide6 # noqa
from PySide6.QtCore import QByteArray, QDate, QDateTime, QTime, QLine, QLineF
from PySide6.QtCore import Qt, QSize, QSizeF, QRect, QRectF, QPoint, QPointF
try:
class QDateTimeReprCopy(ReprCopyHelper, unittest.TestCase):
def setUp(self):
- self.original = QDateTime(2010, 5, 18, 10, 24, 45, 223, Qt.LocalTime)
+ self.original = QDateTime(2010, 5, 18, 10, 24, 45, 223, Qt.TimeSpec.LocalTime)
class QSizeReprCopy(ReprCopyHelper, unittest.TestCase):
<file alias="sample.png">sample.png</file>
</qresource>
</RCC>
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
# Resource object code
#
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
+import gc
import os
import sys
import unittest
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
-from helper.usesqapplication import UsesQApplication
+from helper.usesqapplication import UsesQApplication # noqa: E402
-from PySide6.QtCore import (QCoreApplication, QObject, QStringListModel,
+from PySide6.QtCore import (QCoreApplication, QObject, QStringListModel, # noqa: E402
QTimer, Signal, Slot, Qt)
model = QStringListModel(data_list,
destroyed=destroyed_handler,
dataChanged=changed_handler)
- model.setData(model.index(0, 0), "bla", Qt.EditRole)
+ model.setData(model.index(0, 0), "bla", Qt.ItemDataRole.EditRole)
del model
+ # PYSIDE-535: Need to collect garbage twice in PyPy to trigger deletion
+ gc.collect()
+ gc.collect()
self.assertTrue(was_changed)
self.assertTrue(was_destroyed)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import os
import sys
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
from PySide6.QtCore import Property, QSize
is_pypy = hasattr(sys, "pypy_version_info")
if not is_pypy:
- from PySide6.support import feature
+ from PySide6.support import feature # noqa
"""
snake_prop_feature_test.py
@unittest.skipIf(is_pypy, "__feature__ cannot yet be used with PyPy")
class FeatureTest(unittest.TestCase):
def setUp(self):
- qApp or QApplication()
+ qApp or QApplication() # noqa
feature.reset()
def tearDown(self):
feature.reset()
- qApp.shutdown()
+ qApp.shutdown() # noqa
def testRenamedFunctions(self):
window = Window()
window.setWindowTitle('camelCase')
# and now the same with snake_case enabled
- from __feature__ import snake_case
+ from __feature__ import snake_case # noqa
# Works with the same window! window = Window()
window.set_window_title('snake_case')
with self.assertRaises(AttributeError):
window.modal
- from __feature__ import snake_case, true_property
+ from __feature__ import snake_case, true_property # noqa
# PYSIDE-1548: Make sure that another import does not clear the features.
- import sys
+ import sys # noqa
self.assertTrue(isinstance(QWidget.modal, property))
self.assertTrue(isinstance(window.modal, bool))
window.modal
def testClassProperty(self):
- from __feature__ import snake_case, true_property
+ from __feature__ import snake_case, true_property # noqa
# We check the class...
self.assertEqual(type(QApplication.quit_on_last_window_closed), bool)
x = QApplication.quit_on_last_window_closed
QApplication.quit_on_last_window_closed = not x
self.assertEqual(QApplication.quit_on_last_window_closed, not x)
# ... and now the instance.
- self.assertEqual(type(qApp.quit_on_last_window_closed), bool)
- x = qApp.quit_on_last_window_closed
- qApp.quit_on_last_window_closed = not x
- self.assertEqual(qApp.quit_on_last_window_closed, not x)
+ self.assertEqual(type(qApp.quit_on_last_window_closed), bool) # noqa
+ x = qApp.quit_on_last_window_closed # noqa
+ qApp.quit_on_last_window_closed = not x # noqa
+ self.assertEqual(qApp.quit_on_last_window_closed, not x) # noqa
# make sure values are equal
- self.assertEqual(qApp.quit_on_last_window_closed,
+ self.assertEqual(qApp.quit_on_last_window_closed, # noqa
QApplication.quit_on_last_window_closed)
def testUserClassNotAffected(self):
self.assertTrue(isinstance(UserClass.someFunc2, FunctionType))
self.assertTrue(isinstance(UserClass.addAction, MethodDescriptorType))
- from __feature__ import snake_case
+ from __feature__ import snake_case # noqa
inspect = UserClass.__dict__
self.assertTrue(isinstance(inspect["someFunc1"], FunctionType))
self.assertTrue(isinstance(UserClass.add_action, MethodDescriptorType))
def testTrueProperyCanOverride(self):
- from __feature__ import true_property
+ from __feature__ import true_property # noqa
class CustomWidget(QWidget):
global prop_result
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for overloads involving static and non-static versions of a method.'''
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test case for QObject.signalsBlocked() and blockSignal()'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
if __name__ == '__main__':
unittest.main()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests to test QTranslator and translation in general.'''
if __name__ == '__main__':
unittest.main()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests the presence of unary operator __neg__ on the QPoint class'''
__neg__method_exists = True
try:
neg_qpoint = -self.qpoint
- except:
+ except: # noqa: E722
__neg__method_exists = False
self.assertTrue(__neg__method_exists)
if __name__ == '__main__':
unittest.main()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QString conversion to/from Python Unicode'''
init_test_paths(False)
from PySide6.QtCore import QObject
-from PySide6.QtCore import QByteArray
class UnicodeConversion(unittest.TestCase):
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
self.assertEqual(len(v), 5)
self.assertEqual(type(v[0]), int)
self.assertEqual(type(v[1]), int)
- self.assertEqual(type(v[2]), int)
+ #self.assertEqual(type(v[2]), int)
self.assertEqual(type(v[3]), str)
self.assertEqual(type(v[4]), str)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QDBus'''
from helper.usesqapplication import UsesQApplication
import sys
-from PySide6.QtCore import QCoreApplication
-from PySide6.QtDBus import (QDBusConnection, QDBusConnectionInterface,
- QDBusInterface, QDBusReply)
+from PySide6.QtDBus import QDBusConnection
def service_names():
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QtDataVisualization'''
-# Please add some tests, here
+PYSIDE_TEST(qgraphs_numpy_test.py)
--- /dev/null
+{
+ "files": ["qgraphs_numpy_test.py"]
+}
--- /dev/null
+#!/usr/bin/python
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
+
+'''Test cases for QGraphs/numpy'''
+
+import os
+import sys
+import unittest
+try:
+ import numpy as np
+ HAVE_NUMPY = True
+except ModuleNotFoundError:
+ HAVE_NUMPY = False
+
+from pathlib import Path
+sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
+from init_paths import init_test_paths
+init_test_paths(False)
+
+from helper.usesqapplication import UsesQApplication
+from PySide6.QtGraphs import QLineSeries
+
+
+@unittest.skipUnless(HAVE_NUMPY, "requires numpy")
+class QGraphsNumpyTestCase(UsesQApplication):
+ '''Tests related to QGraphs/numpy'''
+
+ def test(self):
+ """PYSIDE-2313: Verify various types."""
+ line_series = QLineSeries()
+ data_types = [np.short, np.ushort, np.int32, np.uint32,
+ np.int64, np.uint64, np.float32, np.float64]
+ for dt in data_types:
+ print("Testing ", dt)
+ old_size = line_series.count()
+ x_arr = np.array([2], dtype=dt)
+ y_arr = np.array([3], dtype=dt)
+ line_series.appendNp(x_arr, y_arr)
+ size = line_series.count()
+ self.assertEqual(size, old_size + 1)
+ point = line_series.points()[size - 1]
+ self.assertEqual(point.x(), 2)
+ self.assertEqual(point.y(), 3)
+
+
+if __name__ == '__main__':
+ unittest.main()
--- /dev/null
+# Please add some tests, here
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' unit test for BUG #1091 '''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test bug 367: http://bugs.openbossa.org/show_bug.cgi?id=367'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
def testIt(self):
# We need a qapp otherwise Qt will crash when trying to detect the
# current platform
- app = QGuiApplication([])
- ev1 = QKeyEvent(QEvent.KeyRelease, Qt.Key_Delete, Qt.NoModifier)
- ev2 = QKeyEvent(QEvent.KeyRelease, Qt.Key_Copy, Qt.NoModifier)
- ks = QKeySequence.Delete
+ app = QGuiApplication([]) # noqa: F841
+ ev1 = QKeyEvent(QEvent.Type.KeyRelease, Qt.Key.Key_Delete, Qt.KeyboardModifier.NoModifier)
+ ev2 = QKeyEvent(QEvent.Type.KeyRelease, Qt.Key.Key_Copy, Qt.KeyboardModifier.NoModifier)
+ ks = QKeySequence.StandardKey.Delete
self.assertTrue(ev1.matches(ks))
self.assertFalse(ev2.matches(ks))
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
def testRepr(self):
c = QColor.fromRgb(1, 2, 3, 4)
s = c.spec()
- self.assertEqual(repr(s), repr(QColor.Rgb))
+ self.assertEqual(repr(s), repr(QColor.Spec.Rgb))
def testOutOfBounds(self):
e = MyEvent()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
stringListModel = QStringListModel(['one', 'two'])
idx = stringListModel.index(1, 0)
persistentModelIndex = QPersistentModelIndex(idx)
- stringListModel.data(persistentModelIndex, Qt.DisplayRole)
-
+ stringListModel.data(persistentModelIndex, Qt.ItemDataRole.DisplayRole)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
class TestQBitmap(UsesQApplication):
def testFromDataMethod(self):
- dataBits = bytes('\x38\x28\x38\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\xfe\xfe\x7c\x7c\x38\x38\x10\x10', "UTF-8")
- bim = QBitmap.fromData(QSize(8, 48), dataBits, QImage.Format_Mono) # missing function
+ dataBits = bytes('\x38\x28\x38\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\xfe\xfe\x7c\x7c\x38\x38\x10\x10', "UTF-8") # noqa: E501
+ # missing function
+ bim = QBitmap.fromData(QSize(8, 48), dataBits, QImage.Format.Format_Mono) # noqa: F841
if __name__ == '__main__':
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for PYSIDE-344, imul/idiv are used instead of mul/div, modifying the argument passed in'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
from copy import deepcopy
import os
self.original = QPolygon([QPoint(1, 2), QPoint(3, 4), QPoint(5, 6)])
-# Avoid these tests until get gcc fixed
-# Related bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43247
-"""
class QMatrix2x2DeepCopy(DeepCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix2x2([1, 2, 3, 4])
+
class QMatrix2x3DeepCopy(DeepCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix2x3([1, 2, 3, 4, 5, 6])
+
class QMatrix2x4DeepCopy(DeepCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix2x4([1, 2, 3, 4, 5, 6, 7, 8])
+
class QMatrix3x2DeepCopy(DeepCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix3x2([1, 2, 3, 4, 5, 6])
+
class QMatrix3x3DeepCopy(DeepCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix3x3([1, 2, 3, 4, 5, 6, 7, 8, 9])
+
class QMatrix3x4DeepCopy(DeepCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix3x4([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
+
class QMatrix4x2DeepCopy(DeepCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix4x2([1, 2, 3, 4, 5, 6, 7, 8])
+
class QMatrix4x3DeepCopy(DeepCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix4x3([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
+
class QMatrix4x4DeepCopy(DeepCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix4x4([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
-"""
+
if __name__ == '__main__':
unittest.main()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
class MyFilter(QObject):
def eventFilter(self, obj, event):
- if event.type() == QEvent.KeyPress:
+ if event.type() == QEvent.Type.KeyPress:
pass
return QObject.eventFilter(self, obj, event)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QImage'''
# Acquire resources
super(SetPixelFloat, self).setUp()
self.color = qRgb(255, 0, 0)
- self.image = QImage(200, 200, QImage.Format_RGB32)
+ self.image = QImage(200, 200, QImage.Format.Format_RGB32)
def tearDown(self):
# Release resources
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import importlib
import importlib.util
if __name__ == "__main__":
unittest.main()
-
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QBrush'''
init_test_paths(False)
from PySide6.QtCore import Qt
-from PySide6.QtGui import QColor, QBrush
+from PySide6.QtGui import QColor, QBrush, QConicalGradient
from helper.usesqapplication import UsesQApplication
obj = QBrush(color)
self.assertEqual(obj.color(), color)
- obj = QBrush(Qt.blue)
- self.assertEqual(obj.color(), Qt.blue)
+ obj = QBrush(Qt.GlobalColor.blue)
+ self.assertEqual(obj.color(), Qt.GlobalColor.blue)
+
+ def testGradient(self):
+ """Test type discovery on class hierarchies with non-virtual
+ destructors by specifying a polymorphic-id-expression without
+ polymorphic-name-function."""
+ gradient = QConicalGradient()
+ brush = QBrush(gradient)
+ self.assertEqual(type(brush.gradient()), type(gradient))
if __name__ == '__main__':
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import pickle
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import colorsys
import gc
from init_paths import init_test_paths
init_test_paths(False)
-import PySide6
+# For c.__repr__
+import PySide6 # noqa
from PySide6.QtCore import Qt
from PySide6.QtGui import QColor, QColorConstants
self.assertEqual(self.color.getCmyk(), (170, 85, 0, 195, 80))
def testGetCmykF(self): # not supported by colorsys
- for x, y in zip(self.color.getCmykF(), (170 / 255.0, 85 / 255.0, 0, 195 / 255.0, 80 / 255.0)):
+ for x, y in zip(self.color.getCmykF(),
+ (170 / 255.0, 85 / 255.0, 0, 195 / 255.0, 80 / 255.0)):
self.assertTrue(x - y < 1 / 10000.0)
def testEqualGlobalColor(self):
'''QColor == Qt::GlobalColor'''
- self.assertEqual(QColor(255, 0, 0), Qt.red)
+ self.assertEqual(QColor(255, 0, 0), Qt.GlobalColor.red)
class QColorCopy(unittest.TestCase):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test for Bug 630 - Fails to resolve overload for QCursor(QBitmap, QBitmap, int, int)
http://bugs.openbossa.org/show_bug.cgi?id=630
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtGui import QBitmap, QCursor, QPixmap
+from PySide6.QtGui import QBitmap, QCursor
from helper.usesqapplication import UsesQApplication
class TestQCursor(UsesQApplication):
def testQCursorConstructor(self):
bmp = QBitmap(16, 16)
- cursor = QCursor(bmp, bmp, 16, 16)
+ cursor = QCursor(bmp, bmp, 16, 16) # noqa
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
self.source_pixmap = QPixmap(100, 100)
# PYSIDE-1533: Use Qt.transparent to force Format_ARGB32_Premultiplied
# when converting to QImage in any case.
- self.source_pixmap.fill(Qt.transparent)
+ self.source_pixmap.fill(Qt.GlobalColor.transparent)
self.output_pixmap = QPixmap()
self.buffer = QByteArray()
- self.read_stream = QDataStream(self.buffer, QIODevice.ReadOnly)
- self.write_stream = QDataStream(self.buffer, QIODevice.WriteOnly)
+ self.read_stream = QDataStream(self.buffer, QIODevice.OpenModeFlag.ReadOnly)
+ self.write_stream = QDataStream(self.buffer, QIODevice.OpenModeFlag.WriteOnly)
def testStream(self):
self.write_stream << self.source_pixmap
image = self.output_pixmap.toImage()
pixel = image.pixel(10, 10)
- self.assertEqual(pixel, QColor(Qt.transparent).rgba())
+ self.assertEqual(pixel, QColor(Qt.GlobalColor.transparent).rgba())
self.assertEqual(self.source_pixmap.toImage(), image)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QDesktopServices'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests for inject codes and modifications on QFontMetrics
and QFontMetricsF'''
def testIntDefault(self):
'''QFontMetrics.boundingRect(int, int, int, int, ...) - default args'''
rect = self.metrics.boundingRect(0, 0, 0, 0,
- Qt.TextExpandTabs | Qt.AlignLeft,
- 'PySide by INdT')
+ Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+ 'PySide by Qt Company')
self.assertTrue(isinstance(rect, QRect))
def testIntWithArg(self):
'''QFontMetrics.boundingRect(int, int, int, int, ...) - single arg'''
rect = self.metrics.boundingRect(0, 0, 0, 0,
- Qt.TextExpandTabs | Qt.AlignLeft,
- 'PySide by INdT', 2)
+ Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+ 'PySide by Qt Company', 2)
self.assertTrue(isinstance(rect, QRect))
def testIntWithFull(self):
'''QFontMetrics.boundingRect(int, int, int, int, ...) - all argss'''
rect = self.metrics.boundingRect(0, 0, 0, 0,
- Qt.TextExpandTabs | Qt.AlignLeft,
- 'PySide by INdT', 20, [1, 2, 3, 4, 5])
+ Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+ 'PySide by Qt Company', 20, [1, 2, 3, 4, 5])
self.assertTrue(isinstance(rect, QRect))
def testIntTypeError(self):
'''QFontMetrics.boundingRect(int, int, int, int, ...) - type error'''
self.assertRaises(TypeError, self.metrics.boundingRect, 0, 0, 0, 0,
- Qt.TextExpandTabs | Qt.AlignLeft,
- 'PySide by INdT', 20, ['aaaa', 'ase'])
+ Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+ 'PySide by Qt Company', 20, ['aaaa', 'ase'])
def testQRectDefault(self):
'''QFontMetrics.boundingRect(QRect, ...) - default args'''
arg = QRect(0, 0, 100, 200)
- rect = self.metrics.boundingRect(arg, Qt.TextExpandTabs | Qt.AlignLeft,
- 'PySide by INdT')
+ rect = self.metrics.boundingRect(arg,
+ Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+ 'PySide by Qt Company')
self.assertTrue(isinstance(rect, QRect))
def testQRectWithArg(self):
'''QFontMetrics.boundingRect(QRect, ...) - only tabstops'''
arg = QRect(0, 0, 100, 200)
- rect = self.metrics.boundingRect(arg, Qt.TextExpandTabs | Qt.AlignLeft,
- 'PySide by INdT', 2)
+ rect = self.metrics.boundingRect(arg,
+ Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+ 'PySide by Qt Company', 2)
self.assertTrue(isinstance(rect, QRect))
def testQRectWithFull(self):
'''QFontMetrics.boundingRect(QRect, ...) - all arguments'''
arg = QRect(0, 0, 100, 200)
- rect = self.metrics.boundingRect(arg, Qt.TextExpandTabs | Qt.AlignLeft,
- 'PySide by INdT', 20,
+ rect = self.metrics.boundingRect(arg,
+ Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+ 'PySide by Qt Company', 20,
[1, 2, 3, 4, 5])
self.assertTrue(isinstance(rect, QRect))
'''QFontMetrics.boundingRect(QRect, ...) - type error'''
arg = QRect(0, 0, 100, 200)
self.assertRaises(TypeError, self.metrics.boundingRect, arg,
- Qt.TextExpandTabs | Qt.AlignLeft,
- 'PySide by INdT', 20, ['aaaa', 'ase'])
+ Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+ 'PySide by Qt Company', 20, ['aaaa', 'ase'])
class SizeTest(QFontMetricsTest):
def testDefault(self):
'''QFontMetrics.size - default arguments'''
- size = self.metrics.size(Qt.TextExpandTabs | Qt.TextSingleLine,
- 'PySide by INdT')
+ size = self.metrics.size(Qt.TextFlag.TextExpandTabs | Qt.TextFlag.TextSingleLine,
+ 'PySide by Qt Company')
self.assertTrue(isinstance(size, QSize))
def testWithTabStops(self):
'''QFontMetrics.size - only tabstops'''
- size = self.metrics.size(Qt.TextExpandTabs | Qt.TextSingleLine,
- 'PySide by INdT', 2)
+ size = self.metrics.size(Qt.TextFlag.TextExpandTabs | Qt.TextFlag.TextSingleLine,
+ 'PySide by Qt Company', 2)
self.assertTrue(isinstance(size, QSize))
def testFull(self):
'''QFontMetrics.size - all arguments'''
- size = self.metrics.size(Qt.TextExpandTabs | Qt.TextSingleLine,
- 'PySide by INdT', 2, [1, 2, 3, 4])
+ size = self.metrics.size(Qt.TextFlag.TextExpandTabs | Qt.TextFlag.TextSingleLine,
+ 'PySide by Qt Company', 2, [1, 2, 3, 4])
self.assertTrue(isinstance(size, QSize))
def testTypeError(self):
'''QFontMetrics.size - type error'''
self.assertRaises(TypeError, self.metrics.size,
- Qt.TextExpandTabs | Qt.AlignLeft,
- 'PySide by INdT', 20, ['aaaa', 'ase'])
+ Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+ 'PySide by Qt Company', 20, ['aaaa', 'ase'])
class QFontMetricsFTest(UsesQApplication):
def testQRectDefault(self):
'''QFontMetricsF.boundingRect(QRectF, ...) - default args'''
arg = QRectF(0, 0, 100, 200)
- rect = self.metrics.boundingRect(arg, Qt.TextExpandTabs | Qt.AlignLeft,
- 'PySide by INdT')
+ rect = self.metrics.boundingRect(arg,
+ Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+ 'PySide by Qt Company')
self.assertTrue(isinstance(rect, QRectF))
def testQRectWithArg(self):
'''QFontMetricsF.boundingRect(QRectF, ...) - only tabstops'''
arg = QRectF(0, 0, 100, 200)
- rect = self.metrics.boundingRect(arg, Qt.TextExpandTabs | Qt.AlignLeft,
- 'PySide by INdT', 2)
+ rect = self.metrics.boundingRect(arg,
+ Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+ 'PySide by Qt Company', 2)
self.assertTrue(isinstance(rect, QRectF))
def testQRectWithFull(self):
'''QFontMetricsF.boundingRect(QRectF, ...) - all arguments'''
arg = QRectF(0, 0, 100, 200)
- rect = self.metrics.boundingRect(arg, Qt.TextExpandTabs | Qt.AlignLeft,
- 'PySide by INdT', 20,
+ rect = self.metrics.boundingRect(arg,
+ Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+ 'PySide by Qt Company', 20,
[1, 2, 3, 4, 5])
self.assertTrue(isinstance(rect, QRectF))
'''QFontMetricsF.boundingRect(QRectF, ...) - type error'''
arg = QRectF(0, 0, 100, 200)
self.assertRaises(TypeError, self.metrics.boundingRect, arg,
- Qt.TextExpandTabs | Qt.AlignLeft,
- 'PySide by INdT', 20, ['aaaa', 'ase'])
+ Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+ 'PySide by Qt Company', 20, ['aaaa', 'ase'])
class FSizeTest(QFontMetricsFTest):
def testDefault(self):
'''QFontMetricsF.size - default arguments'''
- size = self.metrics.size(Qt.TextExpandTabs | Qt.TextSingleLine,
- 'PySide by INdT')
+ size = self.metrics.size(Qt.TextFlag.TextExpandTabs | Qt.TextFlag.TextSingleLine,
+ 'PySide by Qt Company')
self.assertTrue(isinstance(size, QSizeF))
def testWithTabStops(self):
'''QFontMetricsF.size - only tabstops'''
- size = self.metrics.size(Qt.TextExpandTabs | Qt.TextSingleLine,
- 'PySide by INdT', 2)
+ size = self.metrics.size(Qt.TextFlag.TextExpandTabs | Qt.TextFlag.TextSingleLine,
+ 'PySide by Qt Company', 2)
self.assertTrue(isinstance(size, QSizeF))
def testFull(self):
'''QFontMetricsF.size - all arguments'''
- size = self.metrics.size(Qt.TextExpandTabs | Qt.TextSingleLine,
- 'PySide by INdT', 2, [1, 2, 3, 4])
+ size = self.metrics.size(Qt.TextFlag.TextExpandTabs | Qt.TextFlag.TextSingleLine,
+ 'PySide by Qt Company', 2, [1, 2, 3, 4])
self.assertTrue(isinstance(size, QSizeF))
def testTypeError(self):
'''QFontMetricsF.size - type error'''
self.assertRaises(TypeError, self.metrics.size,
- Qt.TextExpandTabs | Qt.AlignLeft,
- 'PySide by INdT', 20, ['aaaa', 'ase'])
+ Qt.TextFlag.TextExpandTabs | Qt.AlignmentFlag.AlignLeft,
+ 'PySide by Qt Company', 20, ['aaaa', 'ase'])
class QCharTest(QFontMetricsFTest):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QImage'''
img2.setColorSpace(img0.colorSpace())
self.assertEqual(img0, img2)
- ## test scanLine method
+ # test scanLine method
data1 = img0.scanLine(0)
data2 = img1.scanLine(0)
self.assertEqual(data1, data2)
def testEmptyBuffer(self):
- img = QImage(bytes('', "UTF-8"), 100, 100, QImage.Format_ARGB32)
+ img = QImage(bytes('', "UTF-8"), 100, 100, QImage.Format.Format_ARGB32) # noqa: F841
def testEmptyStringAsBuffer(self):
- img = QImage(bytes('', "UTF-8"), 100, 100, QImage.Format_ARGB32)
+ img = QImage(bytes('', "UTF-8"), 100, 100, QImage.Format.Format_ARGB32) # noqa: F841
def testXpmConstructor(self):
img = QImage(xpm)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QImage/Windows'''
def create_image():
- result = QImage(20, 20, QImage.Format_RGB32)
- result.fill(Qt.white)
+ result = QImage(20, 20, QImage.Format.Format_RGB32)
+ result.fill(Qt.GlobalColor.white)
return result
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# bug #774
# PYSIDE-1735: Remapped from Qt.Modifier to Qt.KeyboardModifier
# Note that Qt.(Keyboard)?Modifier will be no longer IntFlag.
- ks = QKeySequence(Qt.ShiftModifier, Qt.ControlModifier, Qt.Key_P, Qt.Key_R)
- self.assertEqual(ks[0].keyboardModifiers(), Qt.ShiftModifier)
- self.assertEqual(ks[1].keyboardModifiers(), Qt.ControlModifier)
- self.assertEqual(ks[2].key(), Qt.Key_P)
- self.assertEqual(ks[3].key(), Qt.Key_R)
+ ks = QKeySequence(Qt.KeyboardModifier.ShiftModifier, Qt.KeyboardModifier.ControlModifier,
+ Qt.Key.Key_P, Qt.Key.Key_R)
+ self.assertEqual(ks[0].keyboardModifiers(), Qt.KeyboardModifier.ShiftModifier)
+ self.assertEqual(ks[1].keyboardModifiers(), Qt.KeyboardModifier.ControlModifier)
+ self.assertEqual(ks[2].key(), Qt.Key.Key_P)
+ self.assertEqual(ks[3].key(), Qt.Key.Key_R)
def testAutoMnemonic(self):
qt_set_sequence_auto_mnemonic(True)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
class QPainterDrawText(UsesQApplication):
def setUp(self):
super(QPainterDrawText, self).setUp()
- self.image = QImage(32, 32, QImage.Format_ARGB32)
+ self.image = QImage(32, 32, QImage.Format.Format_ARGB32)
self.painter = QPainter(self.image)
self.text = 'teste!'
def testDrawText(self):
# bug #254
rect = self.painter.drawText(100, 100, 100, 100,
- Qt.AlignCenter | Qt.TextWordWrap,
+ Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextWordWrap,
self.text)
self.assertTrue(isinstance(rect, QRect))
def testDrawTextWithRect(self):
# bug #225
rect = QRect(100, 100, 100, 100)
- newRect = self.painter.drawText(rect, Qt.AlignCenter | Qt.TextWordWrap,
+ newRect = self.painter.drawText(rect,
+ Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextWordWrap,
self.text)
self.assertTrue(isinstance(newRect, QRect))
def testDrawTextWithRectF(self):
'''QPainter.drawText(QRectF, ... ,QRectF*) inject code'''
rect = QRectF(100, 52.3, 100, 100)
- newRect = self.painter.drawText(rect, Qt.AlignCenter | Qt.TextWordWrap,
+ newRect = self.painter.drawText(rect,
+ Qt.AlignmentFlag.AlignCenter | Qt.TextFlag.TextWordWrap,
self.text)
self.assertTrue(isinstance(newRect, QRectF))
'''Using qpainter.setBrush with args other than QBrush'''
def testSetBrushGradient(self):
- image = QImage(32, 32, QImage.Format_ARGB32)
+ image = QImage(32, 32, QImage.Format.Format_ARGB32)
with QPainter(image) as painter:
gradient = QLinearGradient(0, 0, 0, 0)
painter.setBrush(gradient)
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
temporaryFile = QTemporaryFile(QDir.tempPath() + "/pdfwriter_test_XXXXXX.pdf")
self.assertTrue(temporaryFile.open())
pdfWriter = QPdfWriter(temporaryFile)
- pdfWriter.setPageLayout(QPageLayout(QPageSize(QPageSize.A4), QPageLayout.Portrait, QMarginsF(10, 10, 10, 10)))
+ pdfWriter.setPageLayout(QPageLayout(QPageSize(QPageSize.PageSizeId.A4),
+ QPageLayout.Orientation.Portrait,
+ QMarginsF(10, 10, 10, 10)))
doc = QTextDocument("Some text")
doc.print_(pdfWriter)
temporaryFile.close()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
def paintEvent(self, event):
with QPainter(self) as painter:
- painter.setPen(Qt.NoPen)
+ painter.setPen(Qt.PenStyle.NoPen)
self.penFromEnum = painter.pen()
- intVal = Qt.NoPen.value
+ intVal = Qt.PenStyle.NoPen.value
painter.setPen(intVal)
self.penFromInteger = painter.pen()
QTimer.singleShot(20, self.close)
style = Qt.PenStyle(0)
cap = Qt.PenCapStyle(0)
join = Qt.PenJoinStyle(0)
- pen = QPen(Qt.blue, width, style, cap, join)
+ pen = QPen(Qt.GlobalColor.blue, width, style, cap, join) # noqa: F841
def testSetPenWithPenStyleEnum(self):
'''Calls QPainter.setPen with both enum and integer. Bug #511.'''
w.show()
w.setTitle("qpen_test")
self.app.exec()
- self.assertEqual(w.penFromEnum.style(), Qt.NoPen)
- self.assertEqual(w.penFromInteger.style(), Qt.SolidLine)
+ self.assertEqual(w.penFromEnum.style(), Qt.PenStyle.NoPen)
+ self.assertEqual(w.penFromInteger.style(), Qt.PenStyle.SolidLine)
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit test for QPixelFormat'''
class QPixelFormatTest(UsesQApplication):
def test(self):
- image = QImage(QSize(200, 200), QImage.Format_ARGB32)
- image.fill(QColor(Qt.red))
+ image = QImage(QSize(200, 200), QImage.Format.Format_ARGB32)
+ image.fill(QColor(Qt.GlobalColor.red))
pixelFormat = image.pixelFormat()
print(pixelFormat.greenSize())
self.assertEqual(pixelFormat.alphaSize(), 8)
self.assertEqual(pixelFormat.bitsPerPixel(), 32)
def testHelpers(self):
- format = qPixelFormatRgba(8, 8, 8, 8, QPixelFormat.UsesAlpha,
- QPixelFormat.AtBeginning, QPixelFormat.Premultiplied,
- QPixelFormat.UnsignedByte)
+ format = qPixelFormatRgba(8, 8, 8, 8, QPixelFormat.AlphaUsage.UsesAlpha,
+ QPixelFormat.AlphaPosition.AtBeginning,
+ QPixelFormat.AlphaPremultiplied.Premultiplied,
+ QPixelFormat.TypeInterpretation.UnsignedByte)
self.assertEqual(format.redSize(), 8)
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
class QPixmapTest(UsesQApplication):
+
+ def setUp(self):
+ super().setUp()
+ self._sample_file = Path(__file__).resolve().parent / 'sample.png'
+
def testQVariantConstructor(self):
obj = QObject()
pixmap = QPixmap()
self.assertTrue(pixmap.size().height(), 20)
def testQStringConstructor(self):
- pixmap = QPixmap("Testing!")
+ pixmap = QPixmap("Testing!") # noqa: F841
def testQPixmapLoadFromDataWithQFile(self):
- f = QFile(os.path.join(os.path.dirname(__file__), 'sample.png'))
- self.assertTrue(f.open(QIODevice.ReadOnly))
+ f = QFile(self._sample_file)
+ self.assertTrue(f.open(QIODevice.OpenModeFlag.ReadOnly))
data = f.read(f.size())
f.close()
pixmap = QPixmap()
self.assertTrue(pixmap.loadFromData(data))
def testQPixmapLoadFromDataWithPython(self):
- data = open(os.path.join(os.path.dirname(__file__), 'sample.png'), 'rb').read()
+ with self._sample_file.open('rb') as f:
+ data = f.read()
pixmap = QPixmap()
self.assertTrue(pixmap.loadFromData(data))
def testFilledImage(self):
'''QPixmap.fill + toImage + image.pixel'''
+ red = QColor(Qt.GlobalColor.red)
pixmap = QPixmap(100, 200)
- pixmap.fill(Qt.red) # Default Qt.white
+ pixmap.fill(red) # Default Qt.GlobalColor.white
self.assertEqual(pixmap.height(), 200)
self.assertEqual(pixmap.width(), 100)
self.assertEqual(image.width(), 100)
pixel = image.pixel(10, 10)
- self.assertEqual(pixel, QColor(Qt.red).rgba())
+ self.assertEqual(pixel, red.rgba())
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
pm3 = QPixmap()
ok = QPixmapCache.find('img', pm3)
self.assertTrue(ok)
- b1 = QPixmapCache.find('img').toImage().bits()
- b2 = pm3.toImage().bits()
+ b1 = QPixmapCache.find('img').toImage().bits() # noqa: F841
+ b2 = pm3.toImage().bits() # noqa: F841
self.assertEqual(QPixmapCache.find('img').toImage().bits(), pm3.toImage().bits())
def testWithKey(self):
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit test for QBackingStore, QRasterWindow and QStaticText'''
def paintEvent(self, event):
clientRect = QRect(QPoint(0, 0), self.size())
with QPainter(self) as painter:
- painter.fillRect(clientRect, QColor(Qt.red))
+ painter.fillRect(clientRect, QColor(Qt.GlobalColor.red))
painter.drawStaticText(QPoint(10, 10), self.text)
def paintEvent(self, event):
with QPainter(self) as painter:
clientRect = QRect(QPoint(0, 0), self.size())
- painter.fillRect(clientRect, QColor(Qt.white))
+ painter.fillRect(clientRect, QColor(Qt.GlobalColor.white))
ctx = QAbstractTextDocumentLayout.PaintContext()
ctx.clip = clientRect
sel = QAbstractTextDocumentLayout.Selection()
cursor = QTextCursor(self.m_document)
- cursor.movePosition(QTextCursor.Start)
- cursor.movePosition(QTextCursor.NextWord, QTextCursor.KeepAnchor)
+ cursor.movePosition(QTextCursor.MoveOperation.Start)
+ cursor.movePosition(QTextCursor.MoveOperation.NextWord, QTextCursor.MoveMode.KeepAnchor)
sel.cursor = cursor
- sel.format.setForeground(Qt.red)
+ sel.format.setForeground(Qt.GlobalColor.red)
ctx.selections = [sel]
self.m_document.documentLayout().draw(painter, ctx)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test the QShortcut constructor'''
self.qapp = QGuiApplication([])
f = Foo()
- self.sc = MyShortcut(QKeySequence(Qt.Key_Return), f, f.slot_of_foo)
- self.scstd = MyShortcut(QKeySequence.Copy, f, f.slot_of_copy)
+ self.sc = MyShortcut(QKeySequence(Qt.Key.Key_Return), f, f.slot_of_foo)
+ self.scstd = MyShortcut(QKeySequence.StandardKey.Copy, f, f.slot_of_copy)
QTimer.singleShot(0, self.init)
self.qapp.exec()
self.assertEqual(f.ok, True)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
for r in range(3):
my_row = model.takeRow(0)
- my_row = None
+ my_row = None # noqa: F841
for c in range(5):
# only rest 1 reference
self.assertEqual(sys.getrefcount(items[r][c]), 2)
if __name__ == '__main__':
unittest.main()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests conversions of QString to and from QKeySequence.'''
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit test for QStyleHints'''
from init_paths import init_test_paths
init_test_paths(False)
-from helper.usesqapplication import UsesQApplication
from PySide6.QtGui import QStyleHints
+from helper.usesqapplication import UsesQApplication
class QStyleHintsTest(UsesQApplication):
def test(self):
- styleHints = self.app.styleHints()
+ styleHints: QStyleHints = self.app.styleHints()
self.assertTrue(styleHints.startDragDistance() > 0)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
-from PySide6.QtGui import QPageRanges, Qt
+from PySide6.QtGui import QPageRanges, Qt # noqa: E402
class QTextDocumentFunctions(unittest.TestCase):
def testFunctions(self):
self.assertFalse(Qt.mightBeRichText('bla'))
self.assertTrue(Qt.mightBeRichText('<html><head/><body><p>bla</p></body></html>'))
- html = Qt.convertFromPlainText("A & B", Qt.WhiteSpaceNormal)
+ html = Qt.convertFromPlainText("A & B", Qt.WhiteSpaceMode.WhiteSpaceNormal)
self.assertEqual(html, '<p>A & B</p>')
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
text = 'foobar'
doc = QTextDocument(text)
b = QBuffer()
- b.open(QBuffer.ReadWrite)
+ b.open(QBuffer.OpenModeFlag.ReadWrite)
writer = QTextDocumentWriter(b, bytes("plaintext", "UTF-8"))
writer.write(doc)
b.close()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
def testTextOption(self):
"""PYSIDE-2088, large enum values causing MSVC issues."""
- v = QTextOption.IncludeTrailingSpaces | QTextOption.ShowTabsAndSpaces
+ v = QTextOption.Flag.IncludeTrailingSpaces | QTextOption.Flag.ShowTabsAndSpaces
self.assertEqual(v.value, 2147483649)
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
init_test_paths(False)
from PySide6.QtCore import QPointF
-from PySide6.QtGui import QTransform, QPolygonF, QPolygonF, QQuaternion, QVector3D
+from PySide6.QtGui import QTransform, QPolygonF, QQuaternion
class QTransformTest(unittest.TestCase):
if __name__ == "__main__":
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from init_paths import init_test_paths
init_test_paths(False)
-import PySide6
-from PySide6.QtCore import QPoint
+# For self.original
+import PySide6 # noqa
from PySide6.QtGui import QMatrix2x2, QMatrix2x3, QMatrix2x4
from PySide6.QtGui import QMatrix3x2, QMatrix3x3, QMatrix3x4
from PySide6.QtGui import QMatrix4x2, QMatrix4x3, QMatrix4x4
from PySide6.QtGui import QVector2D, QVector3D, QVector4D
-from PySide6.QtGui import QColor, QTransform, QKeySequence, QQuaternion
-from PySide6.QtGui import QPolygon
+from PySide6.QtGui import QTransform, QQuaternion
class ReprCopyHelper:
self.original = QVector4D(1, 2, 3, 4)
-# Avoid these tests until get gcc fixed
-# Related bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43247
-"""
class QMatrix2x2ReprCopy(ReprCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix2x2([1, 2, 3, 4])
+
class QMatrix2x3ReprCopy(ReprCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix2x3([1, 2, 3, 4, 5, 6])
+
class QMatrix2x4ReprCopy(ReprCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix2x4([1, 2, 3, 4, 5, 6, 7, 8])
+
class QMatrix3x2ReprCopy(ReprCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix3x2([1, 2, 3, 4, 5, 6])
+
class QMatrix3x3ReprCopy(ReprCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix3x3([1, 2, 3, 4, 5, 6, 7, 8, 9])
+
class QMatrix3x4ReprCopy(ReprCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix3x4([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
+
class QMatrix4x2ReprCopy(ReprCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix4x2([1, 2, 3, 4, 5, 6, 7, 8])
+
class QMatrix4x3ReprCopy(ReprCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix4x3([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
+
class QMatrix4x4ReprCopy(ReprCopyHelper, unittest.TestCase):
def setUp(self):
self.original = QMatrix4x4([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
-"""
+
if __name__ == '__main__':
unittest.main()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test data for QImage'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtHelp import QHelpEngineCore, QHelpSearchEngine, QHelpSearchResult
+from PySide6.QtHelp import QHelpEngineCore, QHelpSearchEngine
from helper.usesqapplication import UsesQApplication
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit test for Location'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QHttp'''
return
size = 256
byte_array = QByteArray(size, '7')
- buffer = QAudioBuffer(byte_array, self._devices[0].preferredFormat())
+ device = self._devices[0]
+ format = device.preferredFormat()
+ # Observed to be "Unknown" on Linux
+ if format.sampleFormat() == QAudioFormat.SampleFormat.Unknown:
+ sample_formats = device.supportedSampleFormats()
+ if sample_formats:
+ format.setSampleFormat(sample_formats[0])
+ format.setSampleRate(48000)
+ buffer = QAudioBuffer(byte_array, format)
self.assertEqual(buffer.byteCount(), 256)
data = buffer.data()
actual_byte_array = QByteArray(bytearray(data))
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QtMultimediaWidgets'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QHttp'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' unit test for BUG #1084 '''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
self.assertTrue(self.server.listen())
self.client = QTcpSocket()
self.client.connected.connect(self.onClientConnect)
- self.client.connectToHost(QHostAddress(QHostAddress.LocalHost), self.server.serverPort())
+ self.client.connectToHost(QHostAddress(QHostAddress.SpecialAddress.LocalHost),
+ self.server.serverPort())
def done(self):
self.serverConnection.close()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QDnsLookup'''
def setUp(self):
self._app = QCoreApplication([])
- self._lookup = QDnsLookup(QDnsLookup.ANY, 'www.qt.io')
+ self._lookup = QDnsLookup(QDnsLookup.Type.ANY, 'www.qt.io')
self._lookup.finished.connect(self._finished)
def tearDown(self):
gc.collect()
def _finished(self):
- if self._lookup.error() == QDnsLookup.NoError:
+ if self._lookup.error() == QDnsLookup.Error.NoError:
nameRecords = self._lookup.canonicalNameRecords()
if nameRecords:
print(nameRecords[0].name())
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for QHostInfo.'''
@Slot(QHostInfo)
def info_received(self, host_info):
name = host_info.hostName()
- if host_info.error() == QHostInfo.NoError:
+ if host_info.error() == QHostInfo.HostInfoError.NoError:
addresses = [a.toString() for a in host_info.addresses()]
addresses_str = ', '.join(addresses)
print(f'"{name}" resolved to {addresses_str}')
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QIPv6Address'''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QPasswordDigestor'''
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QByteArray, QCryptographicHash
+from PySide6.QtCore import QCryptographicHash
from PySide6.QtNetwork import QPasswordDigestor
class TestPasswordDigestor(unittest.TestCase):
def test(self):
- b = QPasswordDigestor.deriveKeyPbkdf1(QCryptographicHash.Sha1,
+ b = QPasswordDigestor.deriveKeyPbkdf1(QCryptographicHash.Algorithm.Sha1,
b'test', b'saltnpep', 10, 20)
self.assertEqual(b.size(), 20)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QTCPServer'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QUdpSocket'''
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QUrl, QObject, SIGNAL, QCoreApplication, QTimer
+from PySide6.QtCore import QCoreApplication
from PySide6.QtNetwork import QUdpSocket, QHostAddress
self.socket = QUdpSocket()
self.server = QUdpSocket()
- self.server.bind(QHostAddress(QHostAddress.LocalHost), 45454)
+ self.server.bind(QHostAddress(QHostAddress.SpecialAddress.LocalHost), 45454)
def tearDown(self):
# Release resources
gc.collect()
def sendPackage(self):
- addr = QHostAddress(QHostAddress.LocalHost)
+ addr = QHostAddress(QHostAddress.SpecialAddress.LocalHost)
self.socket.writeDatagram(bytes('datagram', "UTF-8"), addr, 45454)
def callback(self):
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QtNfc'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit tests for QOpenGLBuffer'''
-import ctypes
import os
import sys
import unittest
init_test_paths(False)
from helper.usesqapplication import UsesQApplication
-from PySide6.QtGui import QOffscreenSurface, QOpenGLContext, QSurface, QWindow
+from PySide6.QtGui import QOffscreenSurface, QOpenGLContext, QSurface, QWindow, QSurfaceFormat
from PySide6.QtOpenGL import QOpenGLBuffer
def createSurface(surfaceClass):
- if surfaceClass == QSurface.Window:
+ if surfaceClass == QSurface.SurfaceClass.Window:
window = QWindow()
- window.setSurfaceType(QWindow.OpenGLSurface)
+ window.setSurfaceType(QWindow.SurfaceType.OpenGLSurface)
window.setGeometry(0, 0, 10, 10)
window.create()
return window
- elif surfaceClass == QSurface.Offscreen:
+ elif surfaceClass == QSurface.SurfaceClass.Offscreen:
# Create a window and get the format from that. For example, if an EGL
# implementation provides 565 and 888 configs for PBUFFER_BIT but only
# 888 for WINDOW_BIT, we may end up with a pbuffer surface that is
# incompatible with the context since it could choose the 565 while the
# window and the context uses a config with 888.
- format = QSurfaceFormat
- if format.redBufferSize() == -1:
+ _format = QSurfaceFormat
+ if _format.redBufferSize() == -1:
window = QWindow()
- window.setSurfaceType(QWindow.OpenGLSurface)
+ window.setSurfaceType(QWindow.SurfaceType.OpenGLSurface)
window.setGeometry(0, 0, 10, 10)
window.create()
- format = window.format()
+ _format = window.format()
offscreenSurface = QOffscreenSurface()
- offscreenSurface.setFormat(format)
+ offscreenSurface.setFormat(_format)
offscreenSurface.create()
return offscreenSurface
return 0
class QOpenGLBufferTest(UsesQApplication):
def testBufferCreate(self):
- surface = createSurface(QSurface.Window)
+ surface = createSurface(QSurface.SurfaceClass.Window)
ctx = QOpenGLContext()
ctx.create()
ctx.makeCurrent(surface)
self.assertTrue(buf.create())
self.assertTrue(buf.isCreated())
- self.assertEqual(buf.type(), QOpenGLBuffer.VertexBuffer)
+ self.assertEqual(buf.type(), QOpenGLBuffer.Type.VertexBuffer)
buf.bind()
buf.allocate(128)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit test for QOpenGLContext, QOpenGLTexture, QOpenGLWindow and related classes'''
from helper.usesqapplication import UsesQApplication
from PySide6.QtCore import QSize, QTimer, Qt
-from PySide6.QtGui import (QColor, QGuiApplication, QImage, QOpenGLContext,
- QSurfaceFormat)
+from PySide6.QtGui import QColor, QImage, QSurfaceFormat
from PySide6.QtOpenGL import QOpenGLTexture, QOpenGLWindow
def initializeGL(self):
profile = QOpenGLVersionProfile()
profile.setVersion(1, 3)
- profile.setProfile(QSurfaceFormat.CompatibilityProfile)
+ profile.setProfile(QSurfaceFormat.OpenGLContextProfile.CompatibilityProfile)
self.m_functions = QOpenGLVersionFunctionsFactory.get(profile)
self.m_functions.initializeOpenGLFunctions()
print("GL_MAX_LIGHTS=", self.m_functions.glGetIntegerv(GL.GL_MAX_LIGHTS))
- image = QImage(QSize(200, 200), QImage.Format_RGBA8888)
- image.fill(QColor(Qt.red))
+ image = QImage(QSize(200, 200), QImage.Format.Format_RGBA8888)
+ image.fill(QColor(Qt.GlobalColor.red))
self.m_texture = QOpenGLTexture(image)
def paintGL(self):
class QOpenGLWindowTest(UsesQApplication):
- # On macOS, glClear(), glViewport() are rejected due to GLbitfield/GLint not being resolved properly
+ # On macOS, glClear(), glViewport() are rejected due to
+ # GLbitfield/GLint not being resolved properly
def test(self):
openGlWindow = OpenGLWindow()
openGlWindow.resize(640, 480)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit test for Positioning'''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
init_test_paths(False)
from PySide6.QtGui import QTextCursor
-from PySide6.QtPrintSupport import QPrinter, QPrinterInfo
-from PySide6.QtWidgets import QLayout, QWidget, QGraphicsLayout, QGraphicsLayoutItem
+from PySide6.QtWidgets import QLayout, QGraphicsLayout, QGraphicsLayoutItem
from helper.usesqapplication import UsesQApplication
if __name__ == "__main__":
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
import sys
-import unittest
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
def main():
- app = QGuiApplication([])
+ app = QGuiApplication([]) # noqa
# reg qml types here
register_qml_types()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''
Test bug 451: http://bugs.openbossa.org/show_bug.cgi?id=451
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
-import unittest
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
from init_paths import init_test_paths
init_test_paths(False)
-from helper.helper import adjust_filename
-
from PySide6.QtCore import QUrl
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlEngine, QQmlComponent
file = Path(__file__).resolve().parent / 'foo.qml'
assert (not file.is_file())
component.loadUrl(QUrl.fromLocalFile(file))
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# Copyright (C) 2011 Thomas Perl <m@thp.io>
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
# Test case for PySide bug 814
# http://bugs.pyside.org/show_bug.cgi?id=814
super().__init__()
def roleNames(self):
- return {Qt.DisplayRole: b'pysideModelData'}
+ return {Qt.ItemDataRole.DisplayRole: b'pysideModelData'}
def rowCount(self, parent=QModelIndex()):
return 3
def data(self, index, role):
- if index.isValid() and role == Qt.DisplayRole:
+ if index.isValid() and role == Qt.ItemDataRole.DisplayRole:
return 'blubb'
return None
if __name__ == '__main__':
unittest.main()
-
delegate: Text { text: pysideModelData }
model: pythonModel
}
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
"""
This is the corrected version for Python 3.
from PySide6.QtCore import Qt, QUrl, QTimer
from PySide6.QtGui import QGuiApplication, QPen
-from PySide6.QtWidgets import QGraphicsItem
from PySide6.QtQml import qmlRegisterType
-from PySide6.QtQuick import QQuickView, QQuickItem, QQuickPaintedItem
+from PySide6.QtQuick import QQuickView, QQuickPaintedItem
paintCalled = False
def paint(self, painter):
global paintCalled
- pen = QPen(Qt.black, 2)
+ pen = QPen(Qt.GlobalColor.black, 2)
painter.setPen(pen)
painter.drawPie(self.boundingRect(), 0, 128)
paintCalled = True
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
"""
This is the now incorrect old version from Python 2.
from PySide6.QtCore import Qt, QUrl, QTimer
from PySide6.QtGui import QGuiApplication, QPen
-from PySide6.QtWidgets import QGraphicsItem
from PySide6.QtQml import qmlRegisterType
-from PySide6.QtQuick import QQuickView, QQuickItem, QQuickPaintedItem
+from PySide6.QtQuick import QQuickView, QQuickPaintedItem
paintCalled = False
pass
-class A(object):
+class A:
__metaclass__ = MetaA
def paint(self, painter):
global paintCalled
- pen = QPen(Qt.black, 2)
+ pen = QPen(Qt.GlobalColor.black, 2)
painter.setPen(pen)
painter.drawPie(self.boundingRect(), 0, 128)
paintCalled = True
# Copyright (C) 2022 The Qt Company Ltd.
# Copyright (C) 2011 Thomas Perl <m@thp.io>
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
# Testcase for PySide bug 847
# Released under the same terms as PySide itself
file = Path(__file__).resolve().parent / 'bug_847.qml'
self.assertTrue(file.is_file())
view.setSource(QUrl.fromLocalFile(file))
- while view.status() == QQuickView.Loading:
+ while view.status() == QQuickView.Status.Loading:
self.app.processEvents()
- self.assertEqual(view.status(), QQuickView.Ready)
+ self.assertEqual(view.status(), QQuickView.Status.Ready)
self.assertTrue(view.rootObject(), quickview_errorstring(view))
view.rootObject().setProperty('pythonObject', view)
if __name__ == '__main__':
unittest.main()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
-import sys
import os
import sys
import unittest
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
-import unittest
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
from init_paths import init_test_paths
init_test_paths(False)
-from helper.helper import adjust_filename
-from helper.usesqapplication import UsesQApplication
-
from PySide6.QtCore import QUrl
from PySide6.QtGui import QGuiApplication
from PySide6.QtQuick import QQuickView
# it CAN NOT crash here
print(item)
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from helper.helper import quickview_errorstring
from helper.usesqapplication import UsesQApplication
-from PySide6.QtCore import QCoreApplication, QTimer, QUrl, Slot
+from PySide6.QtCore import QUrl, Slot
from PySide6.QtQml import QQmlPropertyMap
from PySide6.QtQuick import QQuickView
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''
Test case for bug #442
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
-from helper.helper import quickview_errorstring
-from helper.timedqguiapplication import TimedQGuiApplication
-from PySide6.QtCore import QObject, QUrl, SIGNAL
-from PySide6.QtGui import QColor
-from PySide6.QtQuick import QQuickItem, QQuickView
+from helper.helper import quickview_errorstring # noqa: E402
+from helper.timedqguiapplication import TimedQGuiApplication # noqa: E402
+from PySide6.QtCore import QObject, QUrl, SIGNAL # noqa: E402
+from PySide6.QtQuick import QQuickView # noqa: E402
class TestConnectionWithInvalidSignature(TimedQGuiApplication):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
"""Test grouped properties (PYSIDE-1836)."""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
-from helper.helper import quickview_errorstring
-from helper.usesqapplication import UsesQApplication
+from helper.helper import quickview_errorstring # noqa: E402
+from helper.usesqapplication import UsesQApplication # noqa: E402
-from PySide6.QtCore import Slot, Property, Signal, QObject, QUrl
-from PySide6.QtQml import QJSEngine, qmlRegisterType
-from PySide6.QtQuick import QQuickView
+from PySide6.QtCore import Slot, Property, QObject, QUrl # noqa: E402
+from PySide6.QtQml import QJSEngine, qmlRegisterType # noqa: E402
+from PySide6.QtQuick import QQuickView # noqa: E402
test_error_message = "This is an error."
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for QQmlApplicationEngine'''
from helper.timedqguiapplication import TimedQGuiApplication
-from PySide6.QtCore import QUrl, QObject, QTimer, Qt
+from PySide6.QtCore import QTimer
from PySide6.QtQml import QQmlApplicationEngine
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
-from helper.helper import quickview_errorstring
+from helper.helper import quickview_errorstring # noqa: E402
-from PySide6.QtCore import QObject, QUrl, Slot, QTimer
-from PySide6.QtGui import QGuiApplication
-from PySide6.QtQml import QQmlIncubationController, VolatileBool
-from PySide6.QtQuick import QQuickView
+from PySide6.QtCore import QObject, QUrl, Slot, QTimer # noqa: E402
+from PySide6.QtGui import QGuiApplication # noqa: E402
+from PySide6.QtQml import QQmlIncubationController, VolatileBool # noqa: E402
+from PySide6.QtQuick import QQuickView # noqa: E402
class CustomIncubationController(QObject, QQmlIncubationController):
view = QQuickView()
controller = CustomIncubationController(self)
view.engine().setIncubationController(controller)
- view.setResizeMode(QQuickView.SizeRootObjectToView)
+ view.setResizeMode(QQuickView.ResizeMode.SizeRootObjectToView)
file = Path(__file__).resolve().parent / 'qqmlincubator_incubateWhile.qml'
self.assertTrue(file.is_file())
view.setSource(QUrl.fromLocalFile(file))
root = view.rootObject()
# The QML code will issue an interrupt signal after half of its items are loaded.
root.shouldInterrupt.connect(controller.interrupter)
- res = app.exec()
+ res = app.exec() # noqa: F841
itemsToCreate = root.property("itemsToCreate")
loadedItems = root.property("loadedItems")
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QQmlNetwork'''
init_test_paths(False)
from PySide6.QtCore import QUrl, QTimer
-from PySide6.QtGui import QGuiApplication, QWindow
+from PySide6.QtGui import QGuiApplication
from PySide6.QtQuick import QQuickView
from PySide6.QtQml import QQmlNetworkAccessManagerFactory
from PySide6.QtNetwork import QNetworkAccessManager
self.assertTrue(view.rootObject(), quickview_errorstring(view))
view.show()
- self.assertEqual(view.status(), QQuickView.Ready)
+ self.assertEqual(view.status(), QQuickView.Status.Ready)
timer = QTimer()
timer.timeout.connect(check_done)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QQuickView'''
self.assertTrue(view.rootObject(), quickview_errorstring(view))
view.show()
- self.assertEqual(view.status(), QQuickView.Ready)
+ self.assertEqual(view.status(), QQuickView.Status.Ready)
rootObject = view.rootObject()
self.assertTrue(rootObject)
context = QQmlEngine.contextForObject(rootObject)
self.assertTrue(view.rootObject(), quickview_errorstring(view))
view.show()
- self.assertEqual(view.status(), QQuickView.Ready)
+ self.assertEqual(view.status(), QQuickView.Status.Ready)
if __name__ == '__main__':
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
-from helper.helper import qmlcomponent_errorstring
-from helper.timedqguiapplication import TimedQGuiApplication
+from helper.helper import qmlcomponent_errorstring # noqa: E402
+from helper.timedqguiapplication import TimedQGuiApplication # noqa: E402
-from PySide6.QtCore import Property, QObject, QUrl, qVersion
-from PySide6.QtGui import QGuiApplication, QRasterWindow
-from PySide6.QtQml import (QmlNamedElement, QmlForeign, QQmlEngine,
- QQmlComponent)
+from PySide6.QtCore import QObject, QUrl, qVersion # noqa: E402
+from PySide6.QtGui import QRasterWindow # noqa: 402
+from PySide6.QtQml import QmlNamedElement, QmlForeign, QQmlEngine, QQmlComponent # noqa: E402
"""Test the QmlForeign decorator, letting the QQmlEngine create a QRasterWindow."""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
-from helper.helper import quickview_errorstring
+from helper.helper import quickview_errorstring # noqa: E402
-from PySide6.QtCore import Property, Signal, QTimer, QUrl, QObject, Slot
-from PySide6.QtGui import QGuiApplication
+from PySide6.QtCore import Property, QTimer, QUrl, QObject, Slot # noqa: E402
+from PySide6.QtGui import QGuiApplication # noqa: E402
from PySide6.QtQml import (qmlRegisterSingletonType, qmlRegisterSingletonInstance,
- QmlElement, QmlSingleton, QJSValue)
-from PySide6.QtQuick import QQuickView
+ QmlElement, QmlSingleton, QJSValue) # noqa: E402
+from PySide6.QtQuick import QQuickView # noqa: E402
URI = "Singletons"
def testIt(self):
app = QGuiApplication([])
- qObjectQmlTypeId = qmlRegisterSingletonType(SingletonQObject, URI, 1, 0,
+ qObjectQmlTypeId = qmlRegisterSingletonType(SingletonQObject, URI, 1, 0, # noqa: F841
'SingletonQObjectNoCallback')
qmlRegisterSingletonType(SingletonQObject, URI, 1, 0, 'SingletonQObjectCallback',
singletonQObjectCallback)
qmlRegisterSingletonType(URI, 1, 0, 'SingletonQJSValue', singletonQJSValueCallback)
# Accepts only QObject derived types
- l = [1, 2]
+ _list = [1, 2]
with self.assertRaises(TypeError):
- qmlRegisterSingletonInstance(SingletonQObject, URI, 1, 0, 'SingletonInstance', l)
+ qmlRegisterSingletonInstance(SingletonQObject, URI, 1, 0, 'SingletonInstance', _list)
# Modify value on the instance
s = SingletonQObject()
self.assertTrue(view._singleton_instance_jsvalue_int)
-if __name__ == '__main__': unittest.main()
+if __name__ == '__main__':
+ unittest.main()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
-from helper.helper import quickview_errorstring
+from helper.helper import quickview_errorstring # noqa: E402
-from PySide6.QtCore import Property, QObject, QTimer, QUrl
-from PySide6.QtGui import QGuiApplication, QPen, QColor, QPainter
-from PySide6.QtQml import (qjsEngine, qmlContext, qmlEngine, qmlRegisterType,
+from PySide6.QtCore import Property, QTimer, QUrl # noqa: E402
+from PySide6.QtGui import QGuiApplication, QPen, QColor, QPainter # noqa: E402
+from PySide6.QtQml import (qjsEngine, qmlContext, qmlEngine, # noqa: E402
ListProperty, QmlElement, QmlNamedElement)
-from PySide6.QtQuick import QQuickView, QQuickItem, QQuickPaintedItem
+from PySide6.QtQuick import QQuickView, QQuickItem, QQuickPaintedItem # noqa: E402
QML_IMPORT_NAME = "Charts"
global paintCalled
pen = QPen(self._color, 2)
painter.setPen(pen)
- painter.setRenderHints(QPainter.Antialiasing, True)
+ painter.setRenderHints(QPainter.RenderHint.Antialiasing, True)
painter.drawPie(self.boundingRect(), self._fromAngle * 16, self._angleSpan * 16)
paintCalled = True
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
-from helper.helper import qmlcomponent_errorstring
-
-from PySide6.QtCore import Property, QObject, QUrl
-from PySide6.QtGui import QGuiApplication
-from PySide6.QtQml import QmlElement, QmlUncreatable, QQmlEngine, QQmlComponent
+from PySide6.QtCore import Property, QObject, QUrl # noqa: E402
+from PySide6.QtGui import QGuiApplication # noqa: E402
+from PySide6.QtQml import QmlElement, QmlUncreatable, QQmlEngine, QQmlComponent # noqa: E402
noCreationReason = 'Cannot create an item of type: Uncreatable (expected)'
class TestQmlSupport(unittest.TestCase):
def testIt(self):
- app = QGuiApplication([])
+ app = QGuiApplication([]) # noqa: F841
engine = QQmlEngine()
file = Path(__file__).resolve().parent / 'registeruncreatable.qml'
component = QQmlComponent(engine, QUrl.fromLocalFile(file))
# Check that the uncreatable item produces the correct error
- self.assertEqual(component.status(), QQmlComponent.Error)
+ self.assertEqual(component.status(), QQmlComponent.Status.Error)
errorFound = False
for e in component.errors():
if noCreationReason in e.toString():
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
-from helper.helper import quickview_errorstring
-from helper.timedqguiapplication import TimedQGuiApplication
+from helper.helper import quickview_errorstring # noqa: E402
+from helper.timedqguiapplication import TimedQGuiApplication # noqa: E402
-from PySide6.QtQuick import QQuickView
-from PySide6.QtCore import QObject, Signal, Slot, QUrl, QTimer, Property
-from PySide6.QtQml import QmlElement
+from PySide6.QtQuick import QQuickView # noqa: E402
+from PySide6.QtCore import QObject, Signal, Slot, QUrl # noqa: E402
+from PySide6.QtQml import QmlElement # noqa: E402
QML_IMPORT_NAME = "test.Obj"
QML_IMPORT_MAJOR_VERSION = 1
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import json
import os
Text { text: model.modelData.title }
}
}
-
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
init_test_paths(False)
from helper.timedqapplication import TimedQApplication
-from PySide6.QtCore import QCoreApplication, QObject, Slot, SIGNAL, SLOT
+from PySide6.QtCore import QCoreApplication, QObject, Slot, SLOT
from PySide6.QtScxml import QScxmlStateMachine, QScxmlEvent
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QSensor'''
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
-from PySide6.QtSensors import QSensor, QSensorReading
+from PySide6.QtSensors import QSensor # noqa: E402
class QSensorTest(unittest.TestCase):
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QSerialPort'''
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
-from PySide6.QtSerialPort import QSerialPort, QSerialPortInfo
-from PySide6.QtCore import QIODevice
+from PySide6.QtSerialPort import QSerialPort, QSerialPortInfo # noqa: E402
+from PySide6.QtCore import QIODevice # noqa: E402
class QSerialPortTest(unittest.TestCase):
def testDefaultConstructedPort(self):
serialPort = QSerialPort()
- self.assertEqual(serialPort.error(), QSerialPort.NoError)
+ self.assertEqual(serialPort.error(), QSerialPort.SerialPortError.NoError)
self.assertTrue(not serialPort.errorString() == "")
# properties
- defaultBaudRate = QSerialPort.Baud9600
+ defaultBaudRate = QSerialPort.BaudRate.Baud9600
self.assertEqual(serialPort.baudRate(), defaultBaudRate)
- self.assertEqual(serialPort.baudRate(QSerialPort.Input), defaultBaudRate)
- self.assertEqual(serialPort.baudRate(QSerialPort.Output), defaultBaudRate)
- self.assertEqual(serialPort.dataBits(), QSerialPort.Data8)
- self.assertEqual(serialPort.parity(), QSerialPort.NoParity)
- self.assertEqual(serialPort.stopBits(), QSerialPort.OneStop)
- self.assertEqual(serialPort.flowControl(), QSerialPort.NoFlowControl)
+ self.assertEqual(serialPort.baudRate(QSerialPort.Direction.Input), defaultBaudRate)
+ self.assertEqual(serialPort.baudRate(QSerialPort.Direction.Output), defaultBaudRate)
+ self.assertEqual(serialPort.dataBits(), QSerialPort.DataBits.Data8)
+ self.assertEqual(serialPort.parity(), QSerialPort.Parity.NoParity)
+ self.assertEqual(serialPort.stopBits(), QSerialPort.StopBits.OneStop)
+ self.assertEqual(serialPort.flowControl(), QSerialPort.FlowControl.NoFlowControl)
- self.assertEqual(serialPort.pinoutSignals(), QSerialPort.NoSignal)
+ self.assertEqual(serialPort.pinoutSignals(), QSerialPort.PinoutSignal.NoSignal)
self.assertEqual(serialPort.isRequestToSend(), False)
self.assertEqual(serialPort.isDataTerminalReady(), False)
# QIODevice
- self.assertEqual(serialPort.openMode(), QIODevice.NotOpen)
+ self.assertEqual(serialPort.openMode(), QIODevice.OpenModeFlag.NotOpen)
self.assertTrue(not serialPort.isOpen())
self.assertTrue(not serialPort.isReadable())
self.assertTrue(not serialPort.isWritable())
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QtSql database creation, destruction and queries'''
init_test_paths(False)
from PySide6.QtSql import QSqlDatabase, QSqlQuery, QSqlTableModel
-from PySide6.QtWidgets import QApplication, QWidget
+from PySide6.QtWidgets import QWidget
from helper.usesqapplication import UsesQApplication
# Acquire resources
super().setUp()
self.assertFalse(not QSqlDatabase.drivers(), "installed Qt has no DB drivers")
- self.assertTrue("QSQLITE" in QSqlDatabase.drivers(), "\"QSQLITE\" driver not available in this Qt version")
+ self.assertTrue("QSQLITE" in QSqlDatabase.drivers(),
+ "\"QSQLITE\" driver not available in this Qt version")
self.db = QSqlDatabase.addDatabase("QSQLITE")
self.db.setDatabaseName(":memory:")
self.assertTrue(self.db.open())
# Test table creation, insertion and retrieval
query = QSqlQuery()
query.exec("CREATE TABLE person(id int primary key, "
- "firstname varchar(20), lastname varchar(20))")
+ "firstname varchar(20), lastname varchar(20))")
query.exec("INSERT INTO person VALUES(101, 'George', 'Harrison')")
query.prepare("INSERT INTO person (id, firstname, lastname) "
"VALUES (:id, :firstname, :lastname)")
def testTableModelDeletion(self):
bar = Foo()
- model = bar.model
+ model = bar.model # noqa: F841
del bar
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
gc.collect()
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QVariant::Type converter'''
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from PySide6.QtCore import (QCoreApplication, QObject, QParallelAnimationGroup,
QTimer, SIGNAL)
-from PySide6.QtStateMachine import (QEventTransition, QFinalState, QState,
- QStateMachine, QSignalTransition)
+from PySide6.QtStateMachine import QEventTransition, QFinalState, QState, QStateMachine
def addStates(transition):
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
init_test_paths(False)
from PySide6.QtCore import QCoreApplication, QObject, SIGNAL, QTimer
-from PySide6.QtStateMachine import (QEventTransition, QFinalState, QState,
- QStateMachine, QSignalTransition)
+from PySide6.QtStateMachine import QFinalState, QState, QStateMachine, QSignalTransition
class QStateTest(unittest.TestCase):
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import (QObject, QParallelAnimationGroup,
- QPropertyAnimation, QTimer, SIGNAL)
+from PySide6.QtCore import QParallelAnimationGroup, QTimer
from PySide6.QtStateMachine import (QFinalState, QState, QStateMachine)
from helper.usesqapplication import UsesQApplication
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
def testLoad(self):
tigerPath = os.path.join(os.path.dirname(__file__), 'tiger.svg')
- app = QGuiApplication([])
+ app = QGuiApplication([]) # noqa: F841
fromFile = QSvgRenderer(tigerPath)
self.assertTrue(fromFile.isValid())
tigerFile = QFile(tigerPath)
- tigerFile.open(QFile.ReadOnly)
+ tigerFile.open(QFile.OpenModeFlag.ReadOnly)
tigerData = tigerFile.readAll()
fromContents = QSvgRenderer(tigerData)
self.assertTrue(fromContents.isValid())
if __name__ == '__main__':
unittest.main()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
tigerPath = QDir.cleanPath(f"{directory}/../QtSvg/tiger.svg")
self.assertTrue(QFileInfo.exists(tigerPath))
- app = QApplication([])
+ app = QApplication([]) # noqa: F841
fromFile = QSvgWidget()
fromFile.load(tigerPath)
self.assertTrue(fromFile.renderer().isValid())
tigerFile = QFile(tigerPath)
- tigerFile.open(QFile.ReadOnly)
+ tigerFile.open(QFile.OpenModeFlag.ReadOnly)
tigerData = tigerFile.readAll()
fromContents = QSvgWidget()
fromContents.load(tigerData)
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''QtTest mouse click functionalities'''
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import Qt, QObject
-from PySide6.QtWidgets import QPushButton, QLineEdit
+from PySide6.QtCore import Qt
+from PySide6.QtWidgets import QPushButton
from PySide6.QtTest import QTest
from helper.usesqapplication import UsesQApplication
button.setCheckable(True)
button.setChecked(False)
- QTest.mouseClick(button, Qt.LeftButton)
+ QTest.mouseClick(button, Qt.MouseButton.LeftButton)
self.assertTrue(button.isChecked())
- QTest.mouseClick(button, Qt.LeftButton)
+ QTest.mouseClick(button, Qt.MouseButton.LeftButton)
self.assertFalse(button.isChecked())
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests for QKeyEvent'''
self.processed = False
def eventFilter(self, obj, event):
- if self.widget == obj and event.type() == self.eventType and \
- isinstance(event, QKeyEvent) and event.key() == self.key:
+ if (self.widget == obj and event.type() == self.eventType
+ and isinstance(event, QKeyEvent) and event.key() == self.key):
self.processed = True
return True
def testKeyEvent(self):
widget = QLineEdit()
- key = Qt.Key_A
- eventFilter = KeyEventFilter(widget, QEvent.KeyPress, key)
+ key = Qt.Key.Key_A
+ eventFilter = KeyEventFilter(widget, QEvent.Type.KeyPress, key)
widget.installEventFilter(eventFilter)
QTest.keyClick(widget, key)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''QtTest QSignalSpy'''
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import Qt, QObject, SIGNAL
+from PySide6.QtCore import SIGNAL
from PySide6.QtGui import QStandardItem, QStandardItemModel
-from PySide6.QtTest import QTest, QSignalSpy
+from PySide6.QtTest import QSignalSpy
from helper.usesqapplication import UsesQApplication
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
return "fixed"
def validate(self, input, pos):
- return (QValidator.Acceptable, "fixed", 1)
+ return (QValidator.State.Acceptable, "fixed", 1)
class MyValidator2(QValidator):
return "fixed"
def validate(self, input, pos):
- return (QValidator.Acceptable, "fixed")
+ return (QValidator.State.Acceptable, "fixed")
class MyValidator3(QValidator):
return "fixed"
def validate(self, input, pos):
- return (QValidator.Acceptable,)
+ return (QValidator.State.Acceptable,)
class MyValidator4(QValidator):
return "fixed"
def validate(self, input, pos):
- return QValidator.Acceptable
+ return QValidator.State.Acceptable
class MyValidator5(QValidator):
def validate(self, input, pos):
if input.islower():
- return (QValidator.Intermediate, input, pos)
+ return (QValidator.State.Intermediate, input, pos)
else:
- return (QValidator.Acceptable, input, pos)
+ return (QValidator.State.Acceptable, input, pos)
def fixup(self, input):
return "22"
line.show()
line.setValidator(MyValidator5())
line.setText("foo")
- QTest.keyClick(line, Qt.Key_Return)
+ QTest.keyClick(line, Qt.Key.Key_Return)
self.assertEqual(line.text(), "22")
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QTextToSpeech methods'''
from helper.usesqapplication import UsesQApplication
from PySide6.QtCore import QTimer
-from PySide6.QtWidgets import QApplication
try:
- from PySide6.QtTextToSpeech import QTextToSpeech, QVoice
+ from PySide6.QtTextToSpeech import QTextToSpeech
except ImportError:
print("Skipping test due to missing QtTextToSpeech module")
sys.exit(0)
+<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
- <class>main_window</class>
- <widget class="QMainWindow" name="main_window">
- <widget class="QWidget" name="centralwidget"/>
- <widget class="QMenuBar" name="menubar"/>
- <widget class="QStatusBar" name="statusbar">
- <action name="actionFoo">
- <property name="text">
- <string>foo</string>
- </property>
- </action>
- </widget>
- </widget>
- <resources/>
- <connections/>
+ <class>main_window</class>
+ <widget class="QMainWindow" name="main_window">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>16</width>
+ <height>69</height>
+ </rect>
+ </property>
+ <widget class="QWidget" name="centralwidget"/>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>16</width>
+ <height>26</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QStatusBar" name="statusbar">
+ <action name="actionFoo">
+ <property name="text">
+ <string>foo</string>
+ </property>
+ </action>
+ </widget>
+ </widget>
+ <resources/>
+ <connections/>
</ui>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' unit test for BUG #1060 '''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
loader = MyQUiLoader(w)
filePath = os.path.join(os.path.dirname(__file__), 'minimal.ui')
- ui = loader.load(filePath)
+ ui = loader.load(filePath) # noqa: F841
self.assertEqual(len(loader._widgets), 1)
self.assertEqual(type(loader._widgets[0]), QFrame)
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
-import unittest
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
init_test_paths(False)
from PySide6.QtCore import QTimer
-from PySide6.QtGui import QAction, QActionGroup
-from PySide6.QtWidgets import QApplication, QWidget
+from PySide6.QtGui import QActionGroup
+from PySide6.QtWidgets import QApplication
from PySide6.QtUiTools import QUiLoader
-class Window(object):
+class Window:
def __init__(self):
loader = QUiLoader()
filePath = os.path.join(os.path.dirname(__file__), 'bug_426.ui')
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
-import unittest
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
init_test_paths(False)
from PySide6.QtCore import QObject
-from PySide6.QtWidgets import QApplication, QFrame, QWidget
+from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtUiTools import QUiLoader
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
-import unittest
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
<x>0</x>
<y>0</y>
<width>800</width>
- <height>25</height>
+ <height>26</height>
</rect>
</property>
</widget>
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
-<ui version="4.0" >
- <author></author>
- <comment></comment>
- <exportmacro></exportmacro>
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
<class>MainWindow</class>
- <widget class="QMainWindow" name="MainWindow" >
- <property name="objectName" >
- <string notr="true" >MainWindow</string>
- </property>
- <property name="geometry" >
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<height>600</height>
</rect>
</property>
- <property name="windowTitle" >
+ <property name="windowTitle">
<string>MainWindow</string>
</property>
- <widget class="QMenuBar" name="menubar" />
- <widget class="QWidget" name="centralwidget" />
- <widget class="QStatusBar" name="statusbar" />
+ <widget class="QWidget" name="centralwidget"/>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>26</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
</widget>
- <pixmapfunction></pixmapfunction>
+ <resources/>
<connections/>
</ui>
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
<ui version="4.0">
<class>Form</class>
<widget class="QFrame" name="Form">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>100</width>
+ <height>30</height>
+ </rect>
+ </property>
</widget>
+ <resources/>
+ <connections/>
</ui>
<property name="windowTitle">
<string/>
</property>
-
<widget class="MyWidget" name="custom">
<property name="geometry">
<rect>
</rect>
</property>
</widget>
-
- <widget class="MyWidget" name="custom2">
+ <widget class="MyWidget" name="custom2">
<property name="geometry">
<rect>
<x>10</x>
<height>23</height>
</rect>
</property>
- </widget>
+ </widget>
</widget>
<customwidgets>
<customwidget>
</rect>
</property>
<property name="frameShape">
- <enum>QFrame::StyledPanel</enum>
+ <enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
- <enum>QFrame::Raised</enum>
+ <enum>QFrame::Shadow::Raised</enum>
</property>
<widget class="QPushButton" name="grandson_object">
<property name="geometry">
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
child = w.findChild(QWidget, "child_object")
self.assertNotEqual(child, None)
- self.assertEqual(w.findChild(QWidget, "grandson_object"), child.findChild(QWidget, "grandson_object"))
+ self.assertEqual(w.findChild(QWidget, "grandson_object"),
+ child.findChild(QWidget, "grandson_object"))
def testLoadFileOverride(self):
# PYSIDE-1070, override QUiLoader::createWidget() with parent=None crashes
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
import unittest
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
-from PySide6.QtCore import QBuffer, Qt, QTimer
-from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout
-from PySide6.QtWebEngineWidgets import QWebEngineView
-from PySide6.QtWebEngineCore import (QWebEngineProfile,
- QWebEngineUrlScheme,
- QWebEngineUrlSchemeHandler)
+from PySide6.QtCore import QBuffer, Qt, QTimer # noqa: E402
+from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout # noqa: E402
+from PySide6.QtWebEngineWidgets import QWebEngineView # noqa: E402
+from PySide6.QtWebEngineCore import (QWebEngineProfile, QWebEngineUrlScheme, # noqa: E402
+ QWebEngineUrlSchemeHandler) # noqa: E402
class TestSchemeHandler(QWebEngineUrlSchemeHandler):
class MainTest(unittest.TestCase):
def test_SchemeHandlerRedirect(self):
self._loaded = False
- QApplication.setAttribute(Qt.AA_ShareOpenGLContexts)
+ QApplication.setAttribute(Qt.ApplicationAttribute.AA_ShareOpenGLContexts)
app = QApplication([])
scheme_name = bytes("testpy", "UTF-8")
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
from functools import partial
import os
TEST_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.dirname(TEST_DIR))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
-from PySide6.QtCore import QCoreApplication, QSize, QUrl, Qt
-from PySide6.QtWidgets import QApplication, QVBoxLayout, QWidget
-from PySide6.QtWebEngineCore import QWebEnginePage
-from PySide6.QtWebEngineWidgets import QWebEngineView
+from PySide6.QtCore import QCoreApplication, QSize, QUrl # noqa: E402
+from PySide6.QtWidgets import QApplication, QVBoxLayout, QWidget # noqa: E402
+from PySide6.QtWebEngineCore import QWebEnginePage # noqa: E402
+from PySide6.QtWebEngineWidgets import QWebEngineView # noqa: E402
class MainTest(unittest.TestCase):
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+PYSIDE_TEST(test_webview.py)
+
--- /dev/null
+{
+ "files": ["test_webview.py"]
+}
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
+
+'''Unit test for WebView'''
+
+import os
+import sys
+import unittest
+from pathlib import Path
+
+# Append the necessary paths to sys.path
+sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
+from init_paths import init_test_paths
+init_test_paths(False)
+
+from PySide6.QtWebView import QtWebView
+
+from helper.usesqapplication import UsesQApplication
+
+
+class QWebViewTestCase(UsesQApplication):
+ def test_webview_exists(self):
+ # Initialize QtWebView
+ QtWebView.initialize()
+
+ # Check if QtWebView can be initialized
+ self.assertTrue(QtWebView is not None)
+ print("QtWebView is available in PySide6.")
+
+
+if __name__ == "__main__":
+ unittest.main()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests for QMenuBar.addAction(identifier, callback) calls'''
'''QMenuBar.addAction(id, callback)'''
menubar = QMenuBar()
action = menubar.addAction("Accounts", self._callback)
- action.activate(QAction.Trigger)
- action.setShortcut(Qt.Key_A)
+ action.activate(QAction.ActionEvent.Trigger)
+ action.setShortcut(Qt.Key.Key_A)
self.assertTrue(self.called)
def testWithCppSlot(self):
widget.setCheckable(True)
widget.setChecked(False)
action = menubar.addAction("Accounts", widget, SLOT("toggle()"))
- action.setShortcut(Qt.Key_A)
- action.activate(QAction.Trigger)
+ action.setShortcut(Qt.Key.Key_A)
+ action.activate(QAction.ActionEvent.Trigger)
self.assertTrue(widget.isChecked())
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for PySide API2 support'''
# QIntValidator methods
state, string, number = obj.validate('Test', 0)
- self.assertEqual(state, QValidator.Invalid)
+ self.assertEqual(state, QValidator.State.Invalid)
state, string, number = obj.validate('33', 0)
- self.assertEqual(state, QValidator.Acceptable)
+ self.assertEqual(state, QValidator.State.Acceptable)
def testQSpinBox(self):
obj = WidgetValidatorQSpinBox()
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from testbinding import TestObject
from PySide6.QtWidgets import QApplication
-from PySide6 import __all__ as all
class QApplicationInstance(unittest.TestCase):
self.assertTrue(False)
def testInstanceObject(self):
- self.assertEqual(type(qApp), type(None))
+ self.assertEqual(type(qApp), type(None)) # noqa: F821
TestObject.createApp()
app1 = QApplication.instance()
app2 = QApplication.instance()
app1.setObjectName("MyApp")
self.assertEqual(app1, app2)
self.assertEqual(app2.objectName(), app1.objectName())
- # We no longer support qApp when embedding
- # if len(all) > 3:
- # # an import triggers qApp initialization
- # __import__("PySide6." + all[-1])
- # self.assertEqual(app1, qApp)
app1.destroyed.connect(self.appDestroyed)
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
def replace(self, unit):
old_item = self.test_layout.itemAtPosition(0, 0)
old_label = old_item.widget()
- ref = weakref.ref(old_item, self._destroyed)
+ ref = weakref.ref(old_item, self._destroyed) # noqa: F841
self.test_layout.removeWidget(old_label)
unit.assertRaises(RuntimeError, old_item.widget)
label = QLabel("Label New")
old_label.deleteLater()
- label.setAlignment(Qt.AlignCenter)
+ label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.test_layout.addWidget(label, 0, 0)
def _destroyed(self, obj):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' unit test for BUG #1077 '''
init_test_paths()
from PySide6.QtGui import QSyntaxHighlighter
-from PySide6.QtWidgets import QApplication, QTextEdit, QWidget
+from PySide6.QtWidgets import QApplication, QTextEdit
class Highlighter(QSyntaxHighlighter):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test bug 243: http://bugs.openbossa.org/show_bug.cgi?id=243'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
-import colorsys
import os
import sys
import unittest
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test bug 338: http://bugs.openbossa.org/show_bug.cgi?id=338'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test bug 389: http://bugs.openbossa.org/show_bug.cgi?id=389'''
class BugTest(UsesQApplication):
def testCase(self):
s = QWidget().style()
- i = s.standardIcon(QStyle.SP_TitleBarMinButton)
+ i = s.standardIcon(QStyle.StandardPixmap.SP_TitleBarMinButton)
self.assertEqual(type(i), QIcon)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test bug 400: http://bugs.openbossa.org/show_bug.cgi?id=400'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test bug 367: http://bugs.openbossa.org/show_bug.cgi?id=467'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
class LayoutTransferOwnerShip(unittest.TestCase):
def testBug(self):
- app = QApplication([])
+ app = QApplication([]) # noqa: F841
w = BuggyWidget()
w.setup()
w.show()
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test bug 512: http://bugs.openbossa.org/show_bug.cgi?id=512'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
""" Unittest for bug #547 """
""" http://bugs.openbossa.org/show_bug.cgi?id=547 """
-import sys
import os
import sys
import unittest
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
class TestBug549(unittest.TestCase):
def testBug(self):
- app = QApplication([])
+ app = QApplication([]) # noqa: F841
w = QGraphicsWidget()
w.setContentsMargins(1, 2, 3, 4)
self.assertEqual(w.getContentsMargins(), (1, 2, 3, 4))
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
""" Unittest for bug #575 """
""" http://bugs.openbossa.org/show_bug.cgi?id=575 """
-import sys
import os
import sys
import unittest
class Bug575(unittest.TestCase):
def testPropertyValues(self):
- app = QApplication(sys.argv)
+ app = QApplication(sys.argv) # noqa: F841
textEdit = QPlainTextEdit()
textEdit.insertPlainText("PySide INdT")
selection = QTextEdit.ExtraSelection()
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
""" Unittest for bug #576 """
""" http://bugs.openbossa.org/show_bug.cgi?id=576 """
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testWidgetParent(self):
self._destroyed = False
- app = QApplication(sys.argv)
+ app = QApplication(sys.argv) # noqa: F841
w = QWidget()
b = QPushButton("test")
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test bug 585: http://bugs.openbossa.org/show_bug.cgi?id=585'''
class Bug585(unittest.TestCase):
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testCase(self):
- app = QApplication([])
+ app = QApplication([]) # noqa: F841
self._tree = QTreeWidget()
self._tree.setColumnCount(2)
i1 = QTreeWidgetItem(self._tree, ['1', ])
- i2 = QTreeWidgetItem(self._tree, ['2', ])
+ i2 = QTreeWidgetItem(self._tree, ['2', ]) # noqa: F841
refCount = sys.getrefcount(i1)
# this function return None
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
# trimmed down diagramscene.py to demonstrate crash in sizeHint()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test bug 635: http://bugs.openbossa.org/show_bug.cgi?id=635'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test bug 662: http://bugs.openbossa.org/show_bug.cgi?id=662'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
'''QGraphicsScene::clear() is missing'''
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testIt(self):
- app = QApplication([])
+ app = QApplication([]) # noqa: F841
scene = QGraphicsScene()
hello = scene.addText("Hello")
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test bug 688: http://bugs.openbossa.org/show_bug.cgi?id=688'''
init_test_paths(False)
from helper.usesqapplication import UsesQApplication
-from PySide6.QtGui import QTextFrame, QTextCursor, QTextCharFormat, QFont, QTextFrameFormat
+from PySide6.QtGui import QTextCursor, QTextCharFormat, QFont, QTextFrameFormat
from PySide6.QtWidgets import QTextEdit
def testCase(self):
editor = QTextEdit()
cursor = QTextCursor(editor.textCursor())
- cursor.movePosition(QTextCursor.Start)
+ cursor.movePosition(QTextCursor.MoveOperation.Start)
mainFrame = cursor.currentFrame()
plainCharFormat = QTextCharFormat()
boldCharFormat = QTextCharFormat()
- boldCharFormat.setFontWeight(QFont.Bold)
+ boldCharFormat.setFontWeight(QFont.Weight.Bold)
cursor.insertText("""
Text documents are represented by the
QTextDocument class, rather than by QString objects.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
init_test_paths(False)
from helper.usesqapplication import UsesQApplication
-from PySide6.QtWidgets import QMainWindow, QMenu, QApplication
+from PySide6.QtWidgets import QMainWindow, QMenu
class MainWindow(QMainWindow):
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
app = QApplication([])
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
timer.timeout.connect(timerHandler)
timer.start()
-# This test for a dead lock in QFileDialog.getOpenFileNames, the test fail with a timeout if the dead lock exists.
-QFileDialog.getOpenFileNames(None, "caption", QDir.homePath(), None, "", QFileDialog.DontUseNativeDialog)
+# This test for a dead lock in QFileDialog.getOpenFileNames,
+# the test fail with a timeout if the dead lock exists.
+QFileDialog.getOpenFileNames(None, "caption", QDir.homePath(), None, "",
+ QFileDialog.Option.DontUseNativeDialog)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
class TestBug736 (unittest.TestCase):
def testIt(self):
- app = QApplication([])
- slider = QSlider(Qt.Horizontal)
- slider2 = QSlider(Qt.Horizontal)
+ app = QApplication([]) # noqa: F841
+ slider = QSlider(Qt.Orientation.Horizontal)
+ slider2 = QSlider(Qt.Orientation.Horizontal)
slider2.setMaximum(10)
slider.valueChanged[int].connect(slider2.setMaximum)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from helper.usesqapplication import UsesQApplication
from PySide6.QtCore import QTimer
-from PySide6.QtGui import QPainter, QFont, QFontInfo
+from PySide6.QtGui import QPainter
from PySide6.QtWidgets import QWidget
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
dock1 = QDockWidget()
dock2 = QDockWidget()
-window.addDockWidget(Qt.LeftDockWidgetArea, dock1)
-window.addDockWidget(Qt.LeftDockWidgetArea, dock2)
+window.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, dock1)
+window.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, dock2)
window.tabifyDockWidget(dock1, dock2)
window.show()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from PySide6.QtWidgets import QApplication, QFrame
-class Mixin1(object):
+class Mixin1:
pass
-class Mixin2(object):
+class Mixin2:
pass
-class Mixin3(object):
+class Mixin3:
pass
if __name__ == "__main__":
main()
-
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import sys
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtWidgets import QTableView, QApplication
+from PySide6.QtWidgets import QTableView
from PySide6.QtCore import QAbstractItemModel, QModelIndex
from helper.usesqapplication import UsesQApplication
if __name__ == "__main__":
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
#
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == "__main__":
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
self.assertEqual(self.box.text(), '0')
def sendKbdEvent(self):
- ev = QKeyEvent(QEvent.KeyPress, Qt.Key_A, Qt.NoModifier, 'a')
+ ev = QKeyEvent(QEvent.Type.KeyPress, Qt.Key.Key_A,
+ Qt.KeyboardModifier.NoModifier, 'a')
QCoreApplication.sendEvent(self.box, ev)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
p = QPainter(self)
style = QApplication.style()
option = QStyleOptionButton()
- style.drawControl(QStyle.CE_PushButton, option, p)
+ style.drawControl(QStyle.ControlElement.CE_PushButton, option, p)
self._painted = True
QTimer.singleShot(0, self._emitPainted)
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
s3 = Signal()
-class Window(object):
+class Window:
def __init__(self, s):
self._window = QMainWindow()
- self._window.setAttribute(Qt.WA_DeleteOnClose, True)
+ self._window.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
self._window.setWindowTitle("Demo!")
self._s = s
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
class TestBug941 (unittest.TestCase):
def testIt(self):
- app = QApplication([])
- view = QHeaderView(Qt.Horizontal)
+ app = QApplication([]) # noqa: F841
+ view = QHeaderView(Qt.Orientation.Horizontal)
self.assertTrue(view.sortIndicatorChanged.connect(foo))
- view.sortIndicatorChanged.emit(0, Qt.Vertical) # this can't raise an exception!
+ # this can't raise an exception!
+ view.sortIndicatorChanged.emit(0, Qt.Orientation.Vertical)
if __name__ == '__main__':
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
class TestBug964 (unittest.TestCase):
def testIt(self):
- app = QApplication([])
+ app = QApplication([]) # noqa: F841
model = QStringListModel(["1", "2"])
view = QListView()
view.setModel(model)
view.setCurrentIndex(model.index(0, 0))
- newCursor = view.moveCursor(QAbstractItemView.MoveDown, Qt.NoModifier)
+ newCursor = view.moveCursor(QAbstractItemView.CursorAction.MoveDown,
+ Qt.KeyboardModifier.NoModifier)
self.assertEqual(newCursor.row(), 1)
self.assertEqual(newCursor.column(), 0)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
init_test_paths(False)
from PySide6.QtCore import QSizeF
-from PySide6.QtWidgets import QGraphicsProxyWidget, QSizePolicy, QPushButton, QGraphicsScene, QGraphicsView
+from PySide6.QtWidgets import (QGraphicsProxyWidget, QSizePolicy, QPushButton,
+ QGraphicsScene, QGraphicsView)
from helper.timedqapplication import TimedQApplication
w.setMinimumSize(minimum)
w.setPreferredSize(preferred)
w.setMaximumSize(maximum)
- w.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
+ w.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
return w
prefSize = QSizeF(210, 100)
maxSize = QSizeF(300, 100)
- a = createItem(minSize, prefSize, maxSize, "A")
- b = createItem(minSize, prefSize, maxSize, "B")
- c = createItem(minSize, prefSize, maxSize, "C")
- d = createItem(minSize, prefSize, maxSize, "D")
+ a = createItem(minSize, prefSize, maxSize, "A") # noqa: F841
+ b = createItem(minSize, prefSize, maxSize, "B") # noqa: F841
+ c = createItem(minSize, prefSize, maxSize, "C") # noqa: F841
+ d = createItem(minSize, prefSize, maxSize, "D") # noqa: F841
view = QGraphicsView(scene)
view.show()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
def testCustomProxyWidget(self):
scene = QGraphicsScene()
- proxy = CustomProxy(None, Qt.Window)
+ proxy = CustomProxy(None, Qt.WindowType.Window)
widget = QLabel('Widget')
proxy.setWidget(widget)
- proxy.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
+ proxy.setCacheMode(QGraphicsItem.CacheMode.DeviceCoordinateCache)
scene.addItem(proxy)
scene.setSceneRect(scene.itemsBoundingRect())
view = QGraphicsView(scene)
- view.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
- view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
+ view.setRenderHints(QPainter.RenderHint.Antialiasing
+ | QPainter.RenderHint.SmoothPixmapTransform)
+ view.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.BoundingRectViewportUpdate)
view.show()
- timer = QTimer.singleShot(100, self.app.quit)
+ QTimer.singleShot(100, self.app.quit)
self.app.exec()
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests for calling methods further than the direct parent'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for __hash__'''
if __name__ == '__main__':
unittest.main()
-
-class PysideImportTest2(object):
+class PysideImportTest2:
pass
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
view.setModel(model)
view = QTableView()
createModelAndSetToView(view)
- model = view.model()
+ model = view.model() # noqa: F841
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''(Very) Simple test case for missing names from QtGui and QtWidgets'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test paint event override in python'''
import sys
import unittest
-from textwrap import dedent
-
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
from init_paths import init_test_paths
init_test_paths(False)
from PySide6.QtCore import QTimer
-from PySide6.QtWidgets import QApplication, QWidget
+from PySide6.QtWidgets import QWidget
from helper.usesqapplication import UsesQApplication
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, QTimer, QThread
from PySide6.QtWidgets import QTableView, QWidget
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
This is the example from https://bugreports.qt.io/browse/PYSIDE-772
init_test_paths(False)
from PySide6.QtCore import Signal
-from PySide6.QtWidgets import QApplication, QWidget
-from PySide6 import QtWidgets
+from PySide6.QtWidgets import QApplication, QWidget # noqa F401
+
+from helper.usesqapplication import UsesQApplication
class Harness(QWidget):
self.method___result = self.sender()
-class TestMangle(unittest.TestCase):
-
- def setUp(self):
- QApplication()
-
- def tearDown(self):
- qApp.shutdown()
+class TestMangle(UsesQApplication):
def testPrivateMangle(self):
harness = Harness()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
class QAbstractTextDocumentLayoutTest(UsesQApplication):
- objectType = QTextFormat.UserObject + 1
+ objectType = QTextFormat.ObjectTypes.UserObject + 1
def foo(self):
fmt = QTextCharFormat()
self.textEdit.show()
interface = Foo()
- self.textEdit.document().documentLayout().registerHandler(QAbstractTextDocumentLayoutTest.objectType, interface)
+ self.textEdit.document().documentLayout().registerHandler(
+ QAbstractTextDocumentLayoutTest.objectType, interface)
QTimer.singleShot(0, self.foo)
self.app.exec()
if __name__ == "__main__":
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QAccessible::installFactory().'''
return None
def backgroundColor(self):
- return QColor(Qt.white)
+ return QColor(Qt.GlobalColor.white)
def child(self, index):
return None
return None
def foregroundColor(self):
- return QColor(Qt.black)
+ return QColor(Qt.GlobalColor.black)
def indexOfChild(self, child):
return -1
def setUp(self):
super().setUp()
QAccessible.installFactory(accessible_factory)
- window = Window()
+ window = Window() # noqa: F841
def testLineEdits(self):
window = Window()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
"""
The bug was caused by this commit:
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test the presence of qApp Macro'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtWidgets import QApplication, QPushButton, QWidget, QSpinBox
+from PySide6.QtWidgets import QApplication, QPushButton
class QApplicationDelete(unittest.TestCase):
def testQPushButton(self):
# QApplication deleted before QPushButton
a = QApplication([])
- b = QPushButton('aaaa')
+ b = QPushButton('aaaa') # noqa: F841
del a
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
gc.collect()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
-from PySide6.QtCore import QObject
-from PySide6.QtWidgets import QInputDialog
+from PySide6.QtCore import QObject # noqa: E402
+from PySide6.QtWidgets import QInputDialog # noqa: E402
-from helper.usesqapplication import UsesQApplication
+from helper.usesqapplication import UsesQApplication # noqa: E402
class DynamicSignalTest(UsesQApplication):
def testQDialog(self):
dlg = QInputDialog()
- dlg.setInputMode(QInputDialog.TextInput)
+ dlg.setInputMode(QInputDialog.InputMode.TextInput)
lst = dlg.children()
self.assertTrue(len(lst))
obj = lst[0]
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
self.assertTrue(isinstance(row, int))
self.assertTrue(isinstance(role, QFormLayout.ItemRole))
self.assertEqual(row, 0)
- self.assertEqual(role, QFormLayout.SpanningRole)
+ self.assertEqual(role, QFormLayout.ItemRole.SpanningRole)
def testGetWidgetPosition(self):
formlayout = QFormLayout()
self.assertTrue(isinstance(row, int))
self.assertTrue(isinstance(role, QFormLayout.ItemRole))
self.assertEqual(row, 0)
- self.assertEqual(role, QFormLayout.SpanningRole)
+ self.assertEqual(role, QFormLayout.ItemRole.SpanningRole)
def testGetLayoutPosition(self):
formlayout = QFormLayout()
self.assertTrue(isinstance(row, int))
self.assertTrue(isinstance(role, QFormLayout.ItemRole))
self.assertEqual(row, 0)
- self.assertEqual(role, QFormLayout.SpanningRole)
+ self.assertEqual(role, QFormLayout.ItemRole.SpanningRole)
def testTakeRow(self):
window = QMainWindow()
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test cases related to QGraphicsItem and subclasses'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test cases related to QGraphicsItem and subclasses'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
def testQGraphicsProxyWidget(self):
scene = QGraphicsScene()
- proxy = QGraphicsProxyWidget(None, Qt.Window)
+ proxy = QGraphicsProxyWidget(None, Qt.WindowType.Window)
widget = QLabel('Widget')
proxy.setWidget(widget)
- proxy.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
+ proxy.setCacheMode(QGraphicsItem.CacheMode.DeviceCoordinateCache)
scene.addItem(proxy)
scene.setSceneRect(scene.itemsBoundingRect())
view = QGraphicsView(scene)
- view.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
- view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
+ view.setRenderHints(QPainter.RenderHint.Antialiasing
+ | QPainter.RenderHint.SmoothPixmapTransform)
+ view.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.BoundingRectViewportUpdate)
view.show()
- timer = QTimer.singleShot(100, self.app.quit)
+ QTimer.singleShot(100, self.app.quit)
self.app.exec()
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Basic test cases for QGraphicsScene'''
init_test_paths(False)
from PySide6.QtCore import QPointF
-from PySide6.QtGui import QPolygonF, QPixmap, QPainterPath, QTransform, QWindow
+from PySide6.QtGui import QPolygonF, QPixmap, QPainterPath, QTransform
from PySide6.QtWidgets import QApplication, QPushButton
from PySide6.QtWidgets import QGraphicsScene
from PySide6.QtWidgets import QGraphicsEllipseItem, QGraphicsLineItem
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QKeySequenceEdit.'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QLabel'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QLayout handling of child widgets references'''
from PySide6.QtWidgets import QHBoxLayout, QVBoxLayout, QGridLayout, QWidget
from PySide6.QtWidgets import QStackedLayout, QFormLayout
-from PySide6.QtWidgets import QApplication, QPushButton, QLabel
+from PySide6.QtWidgets import QPushButton, QLabel
from helper.usesqapplication import UsesQApplication
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testMoveLayout(self):
- l = QHBoxLayout()
+ layout = QHBoxLayout()
self.assertEqual(sys.getrefcount(self.widget1), 2)
- l.addWidget(self.widget1)
+ layout.addWidget(self.widget1)
self.assertEqual(sys.getrefcount(self.widget1), 3)
w = QWidget()
- w.setLayout(l)
+ w.setLayout(layout)
self.assertEqual(sys.getrefcount(self.widget1), 3)
def testHBoxReference(self):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testOwnershipTransfer(self):
b = QPushButton("teste")
- l = MyLayout()
+ layout = MyLayout()
- l.addWidget(b)
+ layout.addWidget(b)
self.assertEqual(sys.getrefcount(b), 2)
w = QWidget()
# transfer ref
- w.setLayout(l)
+ w.setLayout(layout)
self.assertEqual(sys.getrefcount(b), 3)
@unittest.skipUnless(hasattr(sys, "getrefcount"), f"{sys.implementation.name} has no refcount")
def testReferenceTransfer(self):
b = QPushButton("teste")
- l = QHBoxLayout()
+ layout = QHBoxLayout()
# keep ref
- l.addWidget(b)
+ layout.addWidget(b)
self.assertEqual(sys.getrefcount(b), 3)
w = QWidget()
# transfer ref
- w.setLayout(l)
+ w.setLayout(layout)
self.assertEqual(sys.getrefcount(b), 3)
def testMissingFunctions(self):
w = QWidget()
b = QPushButton("test")
- l = MissingItemAtLayout()
+ layout = MissingItemAtLayout()
- l.addWidget(b)
+ layout.addWidget(b)
- self.assertRaises(RuntimeError, w.setLayout, l)
+ self.assertRaises(RuntimeError, w.setLayout, layout)
def testQFormLayout(self):
w = QWidget()
formLayout = QFormLayout()
spacer = QSpacerItem(100, 30)
- formLayout.setItem(0, QFormLayout.SpanningRole, spacer)
+ formLayout.setItem(0, QFormLayout.ItemRole.SpanningRole, spacer)
w.setLayout(formLayout)
w.show()
QTimer.singleShot(10, w.close)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
o.setObjectName("obj")
item = QListWidgetItem("item0")
- item.setData(Qt.UserRole, o)
+ item.setData(Qt.ItemDataRole.UserRole, o)
# item._data = o
self.assertTrue(sys.getrefcount(o), 3)
self.assertTrue(sys.getrefcount(item), 2)
def testIt(self):
lst = QListWidget()
lst.show()
- slot = lambda: lst.removeItemWidget(lst.currentItem())
+ slot = lambda: lst.removeItemWidget(lst.currentItem()) # noqa: E731
lst.addItem(QListWidgetItem("foo"))
QTimer.singleShot(0, slot)
QTimer.singleShot(0, lst.close)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
self.assertEqual(sys.getrefcount(c), 2)
w.setCentralWidget(c)
self.assertEqual(sys.getrefcount(c), 3)
- wr = weakref.ref(c, self.objDel)
+ wr = weakref.ref(c, self.objDel) # noqa: F841
w.setCentralWidget(None)
c = None
self.app.exec()
w.setCentralWidget(c2)
self.assertEqual(sys.getrefcount(c2), 3)
- wr = weakref.ref(c, self.objDel)
+ wr = weakref.ref(c, self.objDel) # noqa: F841
w.setCentralWidget(None)
c = None
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test the QMenu.addAction() method'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for multiple inheritance from 2 QObjects'''
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject
from PySide6.QtGui import QIntValidator, QValidator
from PySide6.QtWidgets import QWidget
# QIntValidator methods
state, string, number = obj.validate('aaaa', 0)
- self.assertEqual(state, QValidator.Invalid)
+ self.assertEqual(state, QValidator.State.Invalid)
state, string, number = obj.validate('33', 0)
- self.assertEqual(state, QValidator.Acceptable)
+ self.assertEqual(state, QValidator.State.Acceptable)
if __name__ == '__main__':
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
w = MyWidget()
w.show()
- timer = QTimer.singleShot(100, self.app.quit)
+ QTimer.singleShot(100, self.app.quit)
self.app.exec()
def buttonCb(self, checked):
def testBoolinSignal(self):
b = QPushButton()
b.setCheckable(True)
- b.setShortcut(Qt.Key_A)
+ b.setShortcut(Qt.Key.Key_A)
self._clicked = False
b.toggled[bool].connect(self.buttonCb)
b.toggle()
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == "__main__":
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
-import sys
import os
import sys
import unittest
from helper.usesqapplication import UsesQApplication
-from PySide6.QtGui import QWindow
from PySide6.QtWidgets import (QApplication, QFontComboBox, QLabel, QProxyStyle,
QStyleFactory, QWidget)
# QFontComboBox is used because it has an QLineEdit created in C++ inside it,
# and if the QWidget.setStyle(style) steals the ownership of the style
# for the C++ originated widget everything will break.
- fontComboBox = QFontComboBox(container)
+ fontComboBox = QFontComboBox(container) # noqa: F841
label = QLabel(container)
label.setText('Label')
style = QStyleFactory.create(QStyleFactory.keys()[0])
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import sys
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QTextEdit and ownership problems.'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QToolbar'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
def testHeader(self):
tree = QTreeView()
- tree.setHeader(QHeaderView(Qt.Horizontal))
+ tree.setHeader(QHeaderView(Qt.Orientation.Horizontal))
self.assertIsNotNone(tree.header())
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
"""
Unit tests for QTreeWidgetItem
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QTimer
from PySide6.QtWidgets import QApplication, QTreeWidget, QTreeWidgetItem
class QTreeWidgetItemTest(unittest.TestCase):
def testClass(self):
- app = QApplication([])
+ app = QApplication([]) # noqa: F841
treewidget = QTreeWidget()
item = QTreeWidgetItem(["Words and stuff"])
item2 = QTreeWidgetItem(["More words!"])
if __name__ == "__main__":
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
return value
-class Sequence(object):
+class Sequence:
# Having the __getitem__ method on a class transform the Python
# type to a PySequence.
# Before the patch: aa75437f9119d997dd290471ac3e2cc88ca88bf1
"""
PYSIDE-1798: Test enum is obtained correctly when return through QVariant
"""
- self.obj.setProperty("test", Qt.SolidLine)
+ self.obj.setProperty("test", Qt.PenStyle.SolidLine)
self.assertTrue(isinstance(self.obj.property("test"), Qt.PenStyle))
- self.assertEqual(self.obj.property("test"), Qt.SolidLine)
+ self.assertEqual(self.obj.property("test"), Qt.PenStyle.SolidLine)
def testString(self):
self.obj.setProperty("test", "test")
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtWidgets import QWidget, QVBoxLayout, QPushButton, QApplication, QHBoxLayout
+from PySide6.QtWidgets import QWidget, QVBoxLayout, QPushButton, QHBoxLayout
from helper.usesqapplication import UsesQApplication
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
-import sys
import os
import sys
import unittest
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for Reference count when the object is created in c++ side'''
-import gc
import os
import sys
import unittest
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import Qt, QPointF
+from PySide6.QtCore import QPointF
from PySide6.QtGui import QPolygonF
-from PySide6.QtWidgets import QApplication, QGraphicsScene, QGraphicsRectItem, QGraphicsPolygonItem, QGraphicsRectItem
+from PySide6.QtWidgets import QGraphicsScene, QGraphicsPolygonItem, QGraphicsRectItem
from helper.usesqapplication import UsesQApplication
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import inspect
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
class StandardPixmapTest(UsesQApplication):
def testDefaultOptions(self): # Bug 253
- pixmap = self.app.style().standardPixmap(QStyle.SP_DirClosedIcon)
+ pixmap = self.app.style().standardPixmap(QStyle.StandardPixmap.SP_DirClosedIcon)
self.assertTrue(isinstance(pixmap, QPixmap))
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
class Sentinel():
value = 10
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for overriding inherited protected virtual methods'''
#widget.startTimer(500)
#widget.show()
#app.exec()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtWidgets import QGraphicsScene, QGraphicsRectItem, QGraphicsView, QApplication
+from PySide6.QtWidgets import QGraphicsScene, QGraphicsRectItem, QGraphicsView
from PySide6.QtGui import QBrush, QColor
from PySide6.QtCore import QTimer
from helper.usesqapplication import UsesQApplication
view = QGraphicsView(scene)
view.setWindowTitle("virtual_pure_override_test")
- rect = self.createRoundRect(scene)
+ rect = self.createRoundRect(scene) # noqa: F841
view.show()
self.app.exec()
self.assertTrue(qgraphics_item_painted)
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for Virtual functions with wrong return type'''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
# Qt5: this is gone: from PySide6.QtGui import QMacStyle
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtWidgets import QApplication, QLabel, QStyleFactory
-from PySide6.QtCore import QObject
+from PySide6.QtWidgets import QLabel, QStyleFactory
from helper.usesqapplication import UsesQApplication
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import sys
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
Time a repeated Python run
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+"""
+sizebench.py
+
+Benchmark the size reduction of QtCore, QtGui and QtWidgets binaries.
+
+The project will be built twice in release mode (unchecked)
+ - with option "--unoptimize=all"
+ - without any option.
+
+Then values and relative improvement are printed.
+
+No argument: Use a default Python for each platform (author specific).
+
+ --python <python> use that specific python interpreter
+ --dry-run try it first without compilation
+ --pip automatically install the needed modules
+"""
+import argparse
+import os
+import platform
+import re
+import subprocess
+import sys
+
+from ast import literal_eval
+from pathlib import Path
+
+defaults = {
+ "Darwin": "/Users/tismer/.pyenv/versions/3.12.5/bin/python3",
+ "Windows": "d:/py312_64/python.exe",
+ "Linux": "/home/ctismer/.pyenv/versions/3.12.5/bin/python3",
+}
+
+
+def setup_project_dir():
+ look_for = Path("testing")
+ here = Path(__file__).resolve().parent
+ while here / look_for not in here.iterdir():
+ parent = here.parent
+ if parent == here:
+ raise SystemError(look_for + " not found!")
+ here = parent
+ fsp = os.fspath(here)
+ if fsp not in sys.path:
+ sys.path.insert(0, fsp)
+
+
+def get_build_dir():
+ from testing.buildlog import builds
+ if not builds.history:
+ raise
+ return builds.history[-1].build_dir
+
+
+def check_allowed_python_versions(major, minor):
+ from build_scripts.main import config
+ pattern = r'Programming Language :: Python :: (\d+)\.(\d+)'
+ hist = []
+ for line in config.python_version_classifiers:
+ found = re.search(pattern, line)
+ if found:
+ ma = int(found.group(1))
+ mi = int(found.group(2))
+ if major == ma and minor == mi:
+ return True
+ hist.append((ma, mi))
+ raise ValueError(hist)
+
+
+def get_result_size(build_dir):
+ result_dir = Path(build_dir) / "pyside6" / "PySide6"
+ sum = 0
+ awaited = 3
+ got = 0
+ with os.scandir(result_dir) as it:
+ for entry in it:
+ name = entry.name
+ if (name.startswith(("QtCore.", "QtGui.", "QtWidgets."))
+ and name.endswith((".so", ".pyd"))):
+ size = entry.stat().st_size
+ print(f"{name=} {size=}")
+ sum += size
+ got += 1
+ if awaited != got:
+ raise ValueError(f"got {got} values, expected {awaited}")
+ return sum
+
+
+setup_project_dir()
+plat = platform.system()
+options = [
+ "setup.py", "build", "--limited-api=no", "--skip-docs", "--no-qt-tools",
+ "--module-subset=Core,Gui,Widgets"]
+
+options_base = options + ["--unoptimize=all"]
+options_best = options
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--python", "-p", nargs="?")
+ parser.add_argument("--dry-run", "-d", action="store_true")
+ parser.add_argument("--pip", action="store_true", help="""
+ Install the necessary modules automatically, which can save some trouble""")
+ args = parser.parse_args()
+
+ python = args.python or defaults[plat] if plat in defaults else args.python
+ python = Path(python).expanduser()
+
+ if not python.exists:
+ raise ValueError(f"Python executable `{python}` not found")
+ version = subprocess.check_output([python, "-c", "import sys; print(sys.version_info[:2])"])
+ major, minor = literal_eval(version.decode())
+ try:
+ check_allowed_python_versions(major, minor)
+ except ValueError as e:
+ msg = " ".join(f"{ma}.{mi}" for (ma, mi) in e.args[0])
+ raise ValueError(f"Python versions allowed = {msg}, got {major}.{minor}") from None
+
+ needs_imports = ["packaging", "setuptools"]
+ if args.pip:
+ # This way, setuptools seems to install reliably.
+ subprocess.run([python, "-m", "pip", "install"] + needs_imports)
+ subprocess.run([python, "-m", "pip", "uninstall", "-y"] + needs_imports)
+ subprocess.run([python, "-m", "pip", "install"] + needs_imports)
+
+ skip = args.dry_run
+ cmd = [python] + options_base
+ if not skip:
+ subprocess.run(cmd)
+
+ build_dir = get_build_dir()
+ res_base = get_result_size(build_dir)
+
+ cmd = [python] + options_best
+ if not skip:
+ subprocess.run(cmd)
+
+ build_dir = get_build_dir()
+ res_best = get_result_size(build_dir)
+
+ print()
+ print(f"Compiling with {python}")
+ print(f"Platform = {plat}")
+ print(f"base size = {res_base}")
+ print(f"best size = {res_best}")
+ print(f"improvement {(res_base - res_best) / res_base:%}")
+ if skip:
+ raise ValueError("This result is only fake. Please run it again without the "
+ "--dry-run argument")
find_package(Qt6 REQUIRED COMPONENTS Widgets)
set(pysidetest_SRC
+connection.h
containertest.cpp containertest.h
flagstest.cpp flagstest.h
hiddenobject.cpp hiddenobject.h
)
set(testbinding_SRC
+${CMAKE_CURRENT_BINARY_DIR}/testbinding/connection_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/testbinding/containertest_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/testbinding/flagsnamespace_classforenum_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/testbinding/testobject_wrapper.cpp
add_dependencies(testbinding pyside6 QtCore QtGui QtWidgets pysidetest)
create_generator_target(testbinding)
+PYSIDE_TEST(mypy_correctness_test.py)
PYSIDE_TEST(constructor_properties_test.py)
PYSIDE_TEST(container_test.py)
PYSIDE_TEST(decoratedslot_test.py)
PYSIDE_TEST(typedef_signal_test.py)
PYSIDE_TEST(version_test.py)
PYSIDE_TEST(mock_as_slot_test.py)
-PYSIDE_TEST(pyenum_relax_options_test.py)
+PYSIDE_TEST(pyenum_relax_options_and_lazy_star_test.py)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
import PySide6
+
# Note:
# "from PySide6 import *" can only be used at module level.
# It is also really not recommended to use. But for testing,
# the "__all__" variable is a great feature!
-
-
class AllModulesImportTest(unittest.TestCase):
def testAllModulesCanImport(self):
# would also work: exec("from PySide6 import *")
for name in PySide6.__all__:
exec(f"import PySide6.{name}")
+ def testAllReappearsAfterDel(self):
+ # This is the only incompatibility that remains:
+ # After __all__ is deleted, it will re-appear.
+ PySide6.__all__ = 42
+ self.assertEqual(PySide6.__dict__["__all__"], 42)
+ del PySide6.__all__
+ self.assertTrue(PySide6.__dict__["__all__"])
+ self.assertNotEqual(PySide6.__dict__["__all__"], 42)
+
if __name__ == '__main__':
unittest.main()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef CONNECTION_H
+#define CONNECTION_H
+
+#include "pysidetest_macros.h"
+
+// PYSIDE-2792, testing converter name clashes with QMetaObject::Connection.
+class Connection
+{
+public:
+ Connection(int handle = 0) noexcept : m_handle(handle) {}
+
+ int handle() const { return m_handle; }
+
+private:
+ int m_handle;
+};
+
+#endif // CONNECTION_H
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import os
import sys
class ConstructorPropertiesTest(unittest.TestCase):
def setUp(self):
- qApp or QApplication()
+ qApp or QApplication() # noqa: F821
if not is_pypy:
feature.reset()
def tearDown(self):
if not is_pypy:
feature.reset()
- qApp.shutdown()
+ qApp.shutdown() # noqa: F821
# PYSIDE-1019: First property extension was support by the constructor.
def testCallConstructor(self):
label = QLabel(
- frameStyle=QFrame.Panel | QFrame.Sunken, # QFrame attr, no property
- lineWidth=2, # QFrame property
- text="first line\nsecond line", # QLabel property
- alignment=Qt.AlignBottom | Qt.AlignRight # QLabel property
+ frameStyle=QFrame.Shape.Panel | QFrame.Shadow.Sunken, # QFrame attr, no property
+ lineWidth=2, # QFrame property
+ text="first line\nsecond line", # QLabel property
+ alignment=Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignRight # QLabel property
)
self.assertEqual(label.lineWidth(), 2)
self.assertRaises(AttributeError, lambda: QLabel(
# PYSIDE-1705: The same with snake_case
@unittest.skipIf(is_pypy, "feature switching is not yet possible in PyPy")
def testCallConstructor_snake(self):
- from __feature__ import snake_case
+ from __feature__ import snake_case # noqa: F401
label = QLabel(
- frame_style=QFrame.Panel | QFrame.Sunken, # QFrame attr, no property
- line_width=2, # QFrame property
- text="first line\nsecond line", # QLabel property
- alignment=Qt.AlignBottom | Qt.AlignRight # QLabel property
+ frame_style=QFrame.Shape.Panel | QFrame.Shadow.Sunken, # QFrame attr, no property
+ line_width=2, # QFrame property
+ text="first line\nsecond line", # QLabel property
+ alignment=Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignRight # QLabel property
)
self.assertEqual(label.line_width(), 2)
self.assertRaises(AttributeError, lambda: QLabel(
# PYSIDE-1705: The same with true_property
@unittest.skipIf(is_pypy, "feature switching is not yet possible in PyPy")
def testCallConstructor_prop(self):
- from __feature__ import true_property
+ from __feature__ import true_property # noqa: F401
label = QLabel(
- frameStyle=QFrame.Panel | QFrame.Sunken, # QFrame attr, no property
- lineWidth=2, # QFrame property
- text="first line\nsecond line", # QLabel property
- alignment=Qt.AlignBottom | Qt.AlignRight # QLabel property
+ frameStyle=QFrame.Shape.Panel | QFrame.Shadow.Sunken, # QFrame attr, no property
+ lineWidth=2, # QFrame property
+ text="first line\nsecond line", # QLabel property
+ alignment=Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignRight # QLabel property
)
self.assertEqual(label.lineWidth, 2)
self.assertRaises(AttributeError, lambda: QLabel(
# PYSIDE-1705: The same with snake_case and true_property
@unittest.skipIf(is_pypy, "feature switching is not yet possible in PyPy")
def testCallConstructor_prop_snake(self):
- from __feature__ import snake_case, true_property
+ from __feature__ import snake_case, true_property # noqa: F401
label = QLabel(
- frame_style=QFrame.Panel | QFrame.Sunken, # QFrame attr, no property
+ frame_style=QFrame.Shape.Panel | QFrame.Shadow.Sunken, # QFrame attr, no property
line_width=2, # QFrame property
text="first line\nsecond line", # QLabel property
- alignment=Qt.AlignBottom | Qt.AlignRight # QLabel property
+ alignment=Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignRight # QLabel property
)
self.assertEqual(label.line_width, 2)
self.assertRaises(AttributeError, lambda: QLabel(
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from PySide6.QtGui import QStandardItem, QStandardItemModel
from PySide6.QtWidgets import (QAbstractItemDelegate, QComboBox,
QSpinBox, QStyledItemDelegate,
- QStyleOptionViewItem, QWidget)
+ QStyleOptionViewItem)
id_text = 'This is me'
editor = view.getEditorWidgetFromItemDelegate()
self.assertEqual(type(editor), QComboBox)
self.assertEqual(editor.count(), 1)
- self.assertEqual(editor.itemData(0, Qt.DisplayRole), id_text)
+ self.assertEqual(editor.itemData(0, Qt.ItemDataRole.DisplayRole), id_text)
editor.metaObject()
def testDelegateKeepsReferenceToEditor(self):
editor = view.getEditorWidgetFromItemDelegate()
self.assertEqual(type(editor), QComboBox)
self.assertEqual(editor.count(), 1)
- self.assertEqual(editor.itemData(0, Qt.DisplayRole), id_text)
+ self.assertEqual(editor.itemData(0, Qt.ItemDataRole.DisplayRole), id_text)
editor.metaObject()
def testIntDelegate(self):
for anything that fits into a int. Verify by checking that a spin
box is created as item view editor for int."""
item = QStandardItem()
- item.setData(123123, Qt.EditRole) # <-- QVariant conversion here
+ item.setData(123123, Qt.ItemDataRole.EditRole) # <-- QVariant conversion here
model = QStandardItemModel()
model.appendRow(item)
style_option = QStyleOptionViewItem()
if __name__ == '__main__':
unittest.main()
-
-# Copyright (C) 2022 The Qt Company Ltd.
+# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
self.assertEqual(TestObjectWithoutNamespace.Enum2.Option4, 4)
def testFlagComparisonOperators(self): # PYSIDE-1696, compare to self
- f1 = Qt.AlignHCenter | Qt.AlignBottom
- f2 = Qt.AlignHCenter | Qt.AlignBottom
+ f1 = Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignBottom
+ f2 = Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignBottom
self.assertTrue(f1 == f1)
self.assertTrue(f1 <= f1)
self.assertTrue(f1 >= f1)
self.assertFalse(f1 < f2)
self.assertFalse(f1 > f2)
- self.assertTrue(Qt.AlignHCenter < Qt.AlignBottom)
- self.assertFalse(Qt.AlignHCenter > Qt.AlignBottom)
- self.assertFalse(Qt.AlignBottom < Qt.AlignHCenter)
- self.assertTrue(Qt.AlignBottom > Qt.AlignHCenter)
+ self.assertTrue(Qt.AlignmentFlag.AlignHCenter < Qt.AlignmentFlag.AlignBottom)
+ self.assertFalse(Qt.AlignmentFlag.AlignHCenter > Qt.AlignmentFlag.AlignBottom)
+ self.assertFalse(Qt.AlignmentFlag.AlignBottom < Qt.AlignmentFlag.AlignHCenter)
+ self.assertTrue(Qt.AlignmentFlag.AlignBottom > Qt.AlignmentFlag.AlignHCenter)
# PYSIDE-1735: We are testing that opcodes do what they are supposed to do.
# This is needed in the PyEnum forgiveness mode where we need
# to introspect the code if an Enum was called with no args.
+
+# flake8: noqa
class InvestigateOpcodesTest(unittest.TestCase):
def probe_function1(self):
('LOAD_CONST', 100, 0),
('RETURN_VALUE', 83, None)]
- if sys.version_info[:2] <= (3, 6):
-
- result_2 = [('LOAD_GLOBAL', 116, 0),
- ('LOAD_ATTR', 106, 1),
- ('CALL_FUNCTION', 131, 0),
- ('STORE_FAST', 125, 1),
- ('LOAD_CONST', 100, 0),
- ('RETURN_VALUE', 83, None)]
-
if sys.version_info[:2] == (3, 11):
# Note: Python 3.11 is a bit more complex because it can optimize itself.
# Opcodes are a bit different, and a hidden second code object is used.
self.assertEqual(self.read_code(self.probe_function2, adaptive=True), result_3)
self.assertEqual(self.get_sizes(self.probe_function2, adaptive=True), sizes_3)
- if sys.version_info[:2] >= (3, 12):
+ if sys.version_info[:2] == (3, 12):
- result_1 = [('RESUME', 151, 0),
- ('LOAD_GLOBAL', 116, 0),
- ('LOAD_ATTR', 106, 2),
- ('STORE_FAST', 125, 1),
+ result_1 = [('RESUME', 151, 0),
+ ('LOAD_GLOBAL', 116, 0),
+ ('LOAD_ATTR', 106, 2),
+ ('STORE_FAST', 125, 1),
('RETURN_CONST', 121, 0)]
- result_2 = [('RESUME', 151, 0),
- ('LOAD_GLOBAL', 116, 1),
- ('LOAD_ATTR', 106, 2),
- ('CALL', 171, 0),
- ('STORE_FAST', 125, 1),
+ result_2 = [('RESUME', 151, 0),
+ ('LOAD_GLOBAL', 116, 1),
+ ('LOAD_ATTR', 106, 2),
+ ('CALL', 171, 0),
+ ('STORE_FAST', 125, 1),
('RETURN_CONST', 121, 0)]
+ if sys.version_info[:2] >= (3, 13):
+
+ result_1 = [('RESUME', 149, 0),
+ ('LOAD_GLOBAL', 91, 0),
+ ('LOAD_ATTR', 82, 2),
+ ('STORE_FAST', 110, 1),
+ ('RETURN_CONST', 103, 0)]
+
+ result_2 = [('RESUME', 149, 0),
+ ('LOAD_GLOBAL', 91, 0),
+ ('LOAD_ATTR', 82, 2),
+ ('PUSH_NULL', 34, None),
+ ('CALL', 53, 0),
+ ('STORE_FAST', 110, 1),
+ ('RETURN_CONST', 103, 0)]
+
self.assertEqual(self.read_code(self.probe_function1), result_1)
self.assertEqual(self.read_code(self.probe_function2), result_2)
if __name__ == '__main__':
unittest.main()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
"""
iterable_test.py
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' PYSIDE-315: https://bugreports.qt.io/browse/PYSIDE-315
Test that all signals and slots of a class (including any mixin classes)
from PySide6.QtCore import QObject, Signal, Slot
-class Mixin(object):
+class Mixin:
mixinSignal = Signal()
def __init__(self, *args, **kwargs):
self.mixinTwoSlotCalled = True
-class MixinThree(object):
+class MixinThree:
mixinThreeSignal = Signal()
def __init__(self, *args, **kwargs):
if __name__ == '__main__':
unittest.main()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
""" PYSIDE-1755: https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1755
Tests that a unittest.mock.MagicMock() can be used as a slot for quick
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# This is the original testcase of PYSIDE-1564
-class Age(object):
+class Age:
def __init__(self, age=0, **kwds):
super().__init__(**kwds)
# PYSIDE-2294: Friedemann's test adapted.
# We need to ignore positional args in mixin classes.
-class Ui_X_MainWindow(object): # Emulating uic
+class Ui_X_MainWindow: # Emulating uic
def setupUi(self, MainWindow):
MainWindow.resize(400, 300)
self.lbl = QLabel(self)
# PYSIDE-2654: Additional missing init test.
# This must work if no __init__ is defined (Ui_Form)
-class Ui_Form(object):
+class Ui_Form:
pass
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+import os
+import sys
+import unittest
+import subprocess
+import time
+
+from pathlib import Path
+sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
+from init_paths import init_test_paths
+init_test_paths(False)
+
+try:
+ import mypy # noqa: F401
+ HAVE_MYPY = True
+except ModuleNotFoundError:
+ HAVE_MYPY = False
+
+import PySide6
+from PySide6 import SKIP_MYPY_TEST
+
+qtest_env = os.environ.get("QTEST_ENVIRONMENT", "")
+is_ci = qtest_env == "ci"
+# When we are in COIN, we enforce mypy existence, to prevent misconfigurations.
+USE_MYPY = True if is_ci else HAVE_MYPY
+
+
+@unittest.skipIf(not USE_MYPY, "The mypy test was skipped because mypy is not installed")
+@unittest.skipIf(SKIP_MYPY_TEST, "The mypy test was disabled")
+class MypyCorrectnessTest(unittest.TestCase):
+
+ def setUp(self):
+ self.pyside_dir = Path(PySide6.__file__).parent
+ self.build_dir = self.pyside_dir.parent.parent
+ os.chdir(self.build_dir)
+ self.project_dir = Path(__file__).resolve().parents[4]
+ # For safety about future changes, check that we are sitting above the sources dir.
+ self.assertTrue((self.project_dir / "sources").exists())
+ # Check if the project dir can be written. If so, put the mypy cache there.
+ test_fname = self.project_dir / ".tmp test writable"
+ try:
+ with test_fname.open("w") as f:
+ f.write("works!")
+ f.close()
+ test_fname.unlink()
+ self.cache_dir = self.project_dir / f".pyside{PySide6.__version__}_mypy_cache"
+ except Exception:
+ self.cache_dir = ".mypy_cache" # This is the mypy default.
+
+ def testMypy(self):
+ self.assertTrue(HAVE_MYPY)
+ insert_version = ["--python-version", "3.11"] if sys.version_info[:2] < (3, 11) else []
+ cmd = ([sys.executable, "-m", "mypy", "--cache-dir", self.cache_dir]
+ + insert_version + [self.pyside_dir])
+ time_pre = time.time()
+ ret = subprocess.run(cmd, capture_output=True)
+ time_post = time.time()
+ for line in ret.stdout.decode("utf-8").split("\n"):
+ print(line)
+ print(f"Time used for mypy test = {(time_post - time_pre):.5} s")
+ self.assertEqual(ret.returncode, 0)
+
+
+if __name__ == '__main__':
+ unittest.main()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6 import *
+from PySide6 import * # noqa: F401,F403
from PySide6.support.signature import get_signature
for modname, mod in sys.modules.items():
- # Python 2 leaves "None" in the dict.
- if modname.startswith("PySide6.") and mod is not None:
+ if modname.startswith("PySide6."):
print("importing", modname)
exec("import " + modname)
+# To help linters
+import PySide6.QtCore
+import PySide6.QtGui
+import PySide6.QtWidgets
+
# This test tests the existence and callability of the newly existing functions,
# after the inheritance was made complete in the course of PYSIDE-331.
PySide6.QtCore.QAbstractListModel().parent()
PySide6.QtCore.QAbstractTableModel().parent()
PySide6.QtCore.QFile().resize(qint64)
- m = PySide6.QtCore.QMutex(); m.tryLock(); m.unlock() # prevent a message "QMutex: destroying locked mutex"
+ # prevent a message "QMutex: destroying locked mutex"
+ m = PySide6.QtCore.QMutex(); m.tryLock(); m.unlock()
PySide6.QtCore.QSortFilterProxyModel().parent()
PySide6.QtCore.QTemporaryFile(tfarg).open(openMode)
"""
new_functions += """
- PySide6.QtGui.QStandardItemModel().insertColumn(int,qModelIndex)
+ PySide6.QtGui.QStandardItemModel().insertColumn(_int,qModelIndex)
PySide6.QtGui.QStandardItemModel().parent()
# PySide6.QtGui.QTextList(qTextDocument).setFormat(qTextFormat) # Segmentation fault: 11
# PySide6.QtGui.QTextTable(qTextDocument).setFormat(qTextFormat) # Segmentation fault: 11
PySide6.QtWidgets.QBoxLayout(direction).addWidget(qWidget)
PySide6.QtWidgets.QColorDialog().open()
PySide6.QtWidgets.QFileDialog().open()
- PySide6.QtWidgets.QFileSystemModel().index(int,int,qModelIndex)
+ PySide6.QtWidgets.QFileSystemModel().index(_int,_int,qModelIndex)
PySide6.QtWidgets.QFileSystemModel().parent()
PySide6.QtWidgets.QFontDialog().open()
PySide6.QtWidgets.QGestureEvent([]).accept()
PySide6.QtWidgets.QGestureEvent([]).ignore()
PySide6.QtWidgets.QGestureEvent([]).isAccepted()
- PySide6.QtWidgets.QGestureEvent([]).setAccepted(bool)
- # PySide6.QtWidgets.QGraphicsView().render(qPaintDevice,qPoint,qRegion,renderFlags) # QPaintDevice: NotImplementedError
+ PySide6.QtWidgets.QGestureEvent([]).setAccepted(_bool)
+ # QPaintDevice: NotImplementedError
+ # PySide6.QtWidgets.QGraphicsView().render(qPaintDevice,qPoint,qRegion,renderFlags)
PySide6.QtWidgets.QGridLayout().addWidget(qWidget)
PySide6.QtWidgets.QInputDialog().open()
PySide6.QtWidgets.QLineEdit().addAction(qAction)
# PySide6.QtPrintSupport.QPageSetupDialog().open() # Segmentation fault: 11
# PySide6.QtPrintSupport.QPrintDialog().open() # opens the dialog, but works
PySide6.QtPrintSupport.QPrintDialog().printer()
- PySide6.QtPrintSupport.QPrintPreviewDialog().open() # note: this prints something, but really shouldn't ;-)
+ # note: this prints something, but really shouldn't ;-)
+ PySide6.QtPrintSupport.QPrintPreviewDialog().open()
""" if "PySide6.QtPrintSupport" in sys.modules else ""
new_functions += """
PySide6.QtHelp.QHelpContentModel().parent()
- # PySide6.QtHelp.QHelpIndexModel().createIndex(int,int,quintptr) # returned NULL without setting an error
- # PySide6.QtHelp.QHelpIndexModel().createIndex(int,int,object()) # returned NULL without setting an error
+ # returns NULL without setting an error
+ # PySide6.QtHelp.QHelpIndexModel().createIndex(_int,_int,quintptr)
+ # returns NULL without setting an error
+ # PySide6.QtHelp.QHelpIndexModel().createIndex(_int,_int,object())
""" if "PySide6.QtHelp" in sys.modules else ""
new_functions += """
break
except AttributeError:
continue
- bool = True
- int = 42
- qint64 = 42
- tfarg = os.path.join(PySide6.QtCore.QDir.tempPath(), "XXXXXX.tmp")
- findStr = 'bla'
- orientation = PySide6.QtCore.Qt.Orientations()
- openMode = PySide6.QtCore.QIODevice.OpenMode(PySide6.QtCore.QIODevice.ReadOnly)
- qModelIndex = PySide6.QtCore.QModelIndex()
- transformationMode = PySide6.QtCore.Qt.TransformationMode()
- qObject = PySide6.QtCore.QObject()
- qPoint = PySide6.QtCore.QPoint()
+ _bool = True # noqa: F841,F405
+ _int = 42 # noqa: F841,F405
+ qint64 = 42 # noqa: F841,F405
+ tfarg = os.path.join(PySide6.QtCore.QDir.tempPath(), "XXXXXX.tmp") # noqa: F841,F405
+ findStr = 'bla' # noqa: F841,F405
+ orientation = PySide6.QtCore.Qt.Orientations() # noqa: F841,F405
+ openMode = PySide6.QtCore.QIODevice.OpenMode(PySide6.QtCore.QIODevice.OpenModeFlag.ReadOnly) # noqa: F841,F405,E501
+ qModelIndex = PySide6.QtCore.QModelIndex() # noqa: F841,F405
+ transformationMode = PySide6.QtCore.Qt.TransformationMode() # noqa: F841,F405
+ qObject = PySide6.QtCore.QObject() # noqa: F841,F405
+ qPoint = PySide6.QtCore.QPoint() # noqa: F841,F405
try:
- PySide6.QtGui
+ PySide6.QtGui # noqa: F405
#qPaintDevice = PySide6.QtGui.QPaintDevice() # NotImplementedError
- qTextDocument = PySide6.QtGui.QTextDocument()
- qTextFormat = PySide6.QtGui.QTextFormat()
- quintptr = 42
- qFont = PySide6.QtGui.QFont()
- qPalette = PySide6.QtGui.QPalette()
+ qTextDocument = PySide6.QtGui.QTextDocument() # noqa: F841,F405
+ qTextFormat = PySide6.QtGui.QTextFormat() # noqa: F841,F405
+ quintptr = 42 # noqa: F841,F405
+ qFont = PySide6.QtGui.QFont() # noqa: F841,F405
+ qPalette = PySide6.QtGui.QPalette() # noqa: F841,F405
except AttributeError:
pass
try:
- PySide6.QtWidgets
- direction = PySide6.QtWidgets.QBoxLayout.Direction()
- qWidget = PySide6.QtWidgets.QWidget()
- qStyleOptionFrame = PySide6.QtWidgets.QStyleOptionFrame()
- qAction = PySide6.QtGui.QAction(qObject)
- renderFlags = PySide6.QtWidgets.QWidget.RenderFlags
+ PySide6.QtWidgets # noqa: F405
+ direction = PySide6.QtWidgets.QBoxLayout.Direction() # noqa: F841,F405
+ qWidget = PySide6.QtWidgets.QWidget() # noqa: F841,F405
+ qStyleOptionFrame = PySide6.QtWidgets.QStyleOptionFrame() # noqa: F841,F405
+ qAction = PySide6.QtGui.QAction(qObject) # noqa: F841,F405
+ renderFlags = PySide6.QtWidgets.QWidget.RenderFlags # noqa: F841,F405
except AttributeError:
pass
palette() without argument.
"""
try:
- qApp = (PySide6.QtWidgets.QApplication.instance() or
- PySide6.QtWidgets.QApplication([]))
+ qApp = ( # noqa: F841
+ PySide6.QtWidgets.QApplication.instance() or PySide6.QtWidgets.QApplication([]) # noqa: F405,E501
+ )
except AttributeError:
unittest.TestCase().skipTest("this test makes only sense if QtWidgets is available.")
- sigs = get_signature(PySide6.QtWidgets.QApplication.palette)
+ sigs = get_signature(PySide6.QtWidgets.QApplication.palette) # noqa: F405
self.assertEqual(len(sigs), 3)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
"""
Test for PySide's Property
from PySide6.QtCore import Property, QObject
-# This are the original imports.
-import sys
-import unittest
has_test = False
try:
from test import support
raise PropertyDel(self._spam)
-class PropertyDocBase(object):
+class PropertyDocBase:
_spam = 1
def _get_spam(self):
@unittest.skipIf(sys.flags.optimize >= 2,
"Docstrings are omitted with -O2 and above")
def test_property_decorator_doc_writable(self):
- class PropertyWritableDoc(object):
+ class PropertyWritableDoc:
@Property(object)
def spam(self):
sub.__class__.spam.__doc__ = 'Spam'
self.assertEqual(sub.__class__.spam.__doc__, 'Spam')
- if has_test: # This test has no support in Python 2
+ if has_test:
@support.refcount_test
def test_refleaks_in___init__(self):
gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
--- /dev/null
+#!/usr/bin/python
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
+
+"""
+PYSIDE-1735: Testing different relax options for Enums
+
+This test uses different configurations and initializes QtCore with it.
+Because re-initialization is not possible, the test uses a subprocess
+for it. This makes the test pretty slow.
+
+Maybe we should implement a way to re-initialize QtCore enums without
+using subprocess, just to speed this up??
+"""
+
+import os
+import sys
+import unittest
+
+from pathlib import Path
+sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
+from init_paths import init_test_paths
+init_test_paths(False)
+
+import subprocess
+import tempfile
+from textwrap import dedent
+
+
+def runtest(program):
+ passed_path = os.fspath(Path(__file__).resolve().parents[1])
+ with tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".py") as fp:
+ preamble = dedent(f"""
+ import os
+ import sys
+ from pathlib import Path
+ sys.path.append({passed_path!r})
+ from init_paths import init_test_paths
+ init_test_paths(False)
+ """)
+ print(preamble, program, file=fp)
+ fp.close()
+ try:
+ ret = subprocess.run([sys.executable, fp.name], check=False, capture_output=True)
+ finally:
+ os.unlink(fp.name)
+ for line in ret.stdout.decode("utf-8").split("\n"):
+ print(line)
+ return ret.returncode == 0
+
+
+def testprog2(option):
+ return runtest(dedent(f"""
+ sys.pyside6_option_python_enum = {option}
+ from PySide6 import QtCore
+ from enum import IntEnum
+ assert(issubclass(QtCore.Qt.DateFormat, IntEnum))
+ """))
+
+
+def testprog4(option):
+ return runtest(dedent(f"""
+ sys.pyside6_option_python_enum = {option}
+ from PySide6 import QtCore
+ QtCore.QtDebugMsg
+ """))
+
+
+def testprog8_16(option):
+ # this test needs flag 16, or the effect would be hidden by forgiving mode
+ return runtest(dedent(f"""
+ sys.pyside6_option_python_enum = {option}
+ from PySide6 import QtCore
+ QtCore.Qt.AlignTop
+ """))
+
+
+def testprog32(option):
+ return runtest(dedent(f"""
+ sys.pyside6_option_python_enum = {option}
+ from PySide6 import QtCore
+ QtCore.Qt.Alignment
+ """))
+
+
+def testprog64(option):
+ return runtest(dedent(f"""
+ sys.pyside6_option_python_enum = {option}
+ from PySide6 import QtCore
+ QtCore.Qt.AlignmentFlag()
+ """))
+
+
+def testprog128_lazy_star(option):
+ return runtest(dedent(f"""
+ sys.pyside6_option_python_enum = {option}
+ from PySide6 import QtCore
+
+ # triggers a lazy star import error:
+ from PySide6.QtCore import *
+ # triggers a NameError
+ QDirListing.DirEntry
+ # End of lazy star
+
+ QtCore.Qt.Key(1234567)
+ """))
+
+
+class TestPyEnumRelaxOption(unittest.TestCase):
+ """
+ This test is a bit involved, because we cannot unload QtCore after it is loaded once.
+ We use subprocess to test different cases, anyway.
+ """
+
+ def test_enumIsIntEnum(self):
+ self.assertTrue(testprog2(2))
+ self.assertFalse(testprog2(4))
+
+ def test_globalDefault(self):
+ self.assertTrue(testprog4(4))
+ self.assertFalse(testprog4(1))
+ self.assertTrue(testprog4(12))
+
+ def test_localDefault(self):
+ self.assertTrue(testprog8_16(8 + 16))
+ self.assertFalse(testprog8_16(0 + 16))
+
+ def test_fakeRenames(self):
+ self.assertTrue(testprog32(1))
+ self.assertFalse(testprog32(32))
+
+ def test_zeroDefault(self):
+ self.assertTrue(testprog64(1))
+ self.assertFalse(testprog64(64))
+
+ def test_Missing(self):
+ self.assertTrue(testprog128_lazy_star(1))
+ self.assertFalse(testprog128_lazy_star(128))
+
+
+if __name__ == "__main__":
+ unittest.main()
+++ /dev/null
-#!/usr/bin/python
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-"""
-PYSIDE-1735: Testing different relax options for Enums
-
-This test uses different configurations and initializes QtCore with it.
-Because re-initialization is not possible, the test uses a subprocess
-for it. This makes the test pretty slow.
-
-Maybe we should implement a way to re-initialize QtCore enums without
-using subprocess, just to speed this up??
-"""
-
-import os
-import sys
-import unittest
-
-from pathlib import Path
-sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
-init_test_paths(False)
-
-import subprocess
-import tempfile
-from textwrap import dedent
-
-
-def runtest(program):
- passed_path = os.fspath(Path(__file__).resolve().parents[1])
- with tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".py") as fp:
- preamble = dedent(f"""
- import os
- import sys
- from pathlib import Path
- sys.path.append({passed_path!r})
- from init_paths import init_test_paths
- init_test_paths(False)
- """)
- print(preamble, program, file=fp)
- fp.close()
- try:
- subprocess.run([sys.executable, fp.name], check=True, capture_output=True)
- return True
- except subprocess.CalledProcessError as e:
- print(f"\ninfo: {e.__class__.__name__}: {e.stderr}")
- return False
- finally:
- os.unlink(fp.name)
-
-
-def testprog2(option):
- return runtest(dedent(f"""
- sys.pyside6_option_python_enum = {option}
- from PySide6 import QtCore
- from enum import IntEnum
- assert(issubclass(QtCore.Qt.DateFormat, IntEnum))
- """))
-
-
-def testprog4(option):
- return runtest(dedent(f"""
- sys.pyside6_option_python_enum = {option}
- from PySide6 import QtCore
- QtCore.QtDebugMsg
- """))
-
-
-def testprog8_16(option):
- # this test needs flag 16, or the effect would be hidden by forgiving mode
- return runtest(dedent(f"""
- sys.pyside6_option_python_enum = {option}
- from PySide6 import QtCore
- QtCore.Qt.AlignTop
- """))
-
-
-def testprog32(option):
- return runtest(dedent(f"""
- sys.pyside6_option_python_enum = {option}
- from PySide6 import QtCore
- QtCore.Qt.Alignment
- """))
-
-
-def testprog64(option):
- return runtest(dedent(f"""
- sys.pyside6_option_python_enum = {option}
- from PySide6 import QtCore
- QtCore.Qt.AlignmentFlag()
- """))
-
-
-def testprog128(option):
- return runtest(dedent(f"""
- sys.pyside6_option_python_enum = {option}
- from PySide6 import QtCore
- QtCore.Qt.Key(1234567)
- """))
-
-
-class TestPyEnumRelaxOption(unittest.TestCase):
- """
- This test is a bit involved, because we cannot unload QtCore after it is loaded once.
- We use subprocess to test different cases, anyway.
- """
-
- def test_enumIsIntEnum(self):
- self.assertTrue(testprog2(2))
- self.assertFalse(testprog2(4))
-
- def test_globalDefault(self):
- self.assertTrue(testprog4(4))
- self.assertFalse(testprog4(1))
- self.assertTrue(testprog4(12))
-
- def test_localDefault(self):
- self.assertTrue(testprog8_16(8 + 16))
- self.assertFalse(testprog8_16(0 + 16))
-
- def test_fakeRenames(self):
- self.assertTrue(testprog32(1))
- self.assertFalse(testprog32(32))
-
- def test_zeroDefault(self):
- self.assertTrue(testprog64(1))
- self.assertFalse(testprog64(64))
-
- def test_Missing(self):
- self.assertTrue(testprog128(1))
- self.assertFalse(testprog128(128))
-
-
-if __name__ == "__main__":
- unittest.main()
#define PYSIDETEST_GLOBAL_H
// PySide global.h file
+#include "connection.h"
#include "containertest.h"
#include "testobject.h"
#include "testview.h"
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from init_paths import init_test_paths
init_test_paths(False)
-import PySide6
+import PySide6 # noqa
# This test tests the new "macro" feature of qApp.
# It also uses the qApp variable to finish the instance and start over.
except ImportError:
QtWidgets = QtGui = QtCore
# qApp is in the builtins
- self.assertEqual(bool(qApp), False)
+ self.assertEqual(bool(qApp), False) # noqa
# and the type is None
- self.assertTrue(qApp is None)
+ self.assertTrue(qApp is None) # noqa
# now we create an application for all cases
classes = (QtCore.QCoreApplication,
QtGui.QGuiApplication,
for klass in classes:
print("CREATED", klass([]), file=fil)
fil.flush()
- qApp.shutdown()
- print("DELETED qApp", qApp, file=fil)
+ qApp.shutdown() # noqa
+ print("DELETED qApp", qApp, file=fil) # noqa
fil.flush()
# creating without deletion raises:
QtCore.QCoreApplication([])
with self.assertRaises(RuntimeError):
QtCore.QCoreApplication([])
- self.assertEqual(QtCore.QCoreApplication.instance(), qApp)
+ self.assertEqual(QtCore.QCoreApplication.instance(), qApp) # noqa
def test_1093(self):
# Test that without creating a QApplication staticMetaObject still exists.
if app is None:
app = QtCore.QCoreApplication([])
self.assertTrue(QtCore.QObject.staticMetaObject is not None)
- qApp.shutdown()
+ qApp.shutdown() # noqa
if __name__ == '__main__':
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import enum
import os
def testQKeySequenceQVariantOperator(self):
# bug #775
- ks = QKeySequence(Qt.ShiftModifier, Qt.ControlModifier, Qt.Key_P, Qt.Key_R)
+ ks = QKeySequence(Qt.KeyboardModifier.ShiftModifier, Qt.KeyboardModifier.ControlModifier,
+ Qt.Key.Key_P, Qt.Key.Key_R)
self.assertEqual(TestObject.checkType(ks), 4107)
def testQKeySequenceMoreVariations(self):
- QAction().setShortcut(Qt.CTRL | Qt.Key_B)
- QAction().setShortcut(Qt.CTRL | Qt.ALT | Qt.Key_B)
- QAction().setShortcut(Qt.CTRL | Qt.AltModifier | Qt.Key_B)
- QAction().setShortcut(QKeySequence(QKeyCombination(Qt.CTRL | Qt.Key_B)))
- QKeySequence(Qt.CTRL | Qt.Key_Q)
+ QAction().setShortcut(Qt.Modifier.CTRL | Qt.Key.Key_B)
+ QAction().setShortcut(Qt.Modifier.CTRL | Qt.Modifier.ALT | Qt.Key.Key_B)
+ QAction().setShortcut(Qt.Modifier.CTRL | Qt.KeyboardModifier.AltModifier | Qt.Key.Key_B)
+ QAction().setShortcut(QKeySequence(QKeyCombination(Qt.Modifier.CTRL | Qt.Key.Key_B)))
+ QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_Q)
def testEnum(self):
# Testing C++ class
# Copyright (C) 2022 The Qt Company Ltd.
# Copyright (C) 2019 Andreas Beckermann
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
-import gc
import os
import sys
import unittest
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' PYSIDE-315: https://bugreports.qt.io/browse/PYSIDE-315
Test that creating a signal in the wrong order triggers a warning. '''
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
"""
PYSIDE-68: Test that signals have a `__get__` function after all.
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
from init_paths import init_test_paths
init_test_paths(True)
-from testbinding import TestObject
-from PySide6.QtCore import QObject, SIGNAL
+# Note: For PYSIDE-2792/testConnectionSignal()), QMetaObject needs to be
+# forcibly created before Connection.
+from PySide6.QtCore import QObject, SIGNAL, Slot, QMetaObject # noqa: F401
+from testbinding import TestObject, Connection
'''Tests the behaviour of signals with default values when emitted from Python.'''
+class Receiver(QObject):
+ """Test receiver for PYSIDE-2792 (testConnectionSignal)."""
+
+ def __init__(self, p=None):
+ super().__init__(p)
+ self.received_handle = -1
+
+ @Slot(Connection)
+ def connectionSlot(self, c):
+ self.received_handle = c.handle()
+
+
class SignalEmissionFromPython(unittest.TestCase):
def setUp(self):
self.assertEqual(self.one_called, 1)
self.assertEqual(self.two_called, 2)
+ def testConnectionSignal(self):
+ """PYSIDE-2792: Test whether a signal parameter of type 'Connection'
+ clashes with QMetaObject.Connection."""
+ receiver = Receiver()
+ qmetaobject_conn = self.obj1.connectionSignal.connect(receiver.connectionSlot)
+ self.assertTrue(qmetaobject_conn)
+ self.obj1.emitConnectionSignal(42)
+ self.assertEqual(receiver.received_handle, 42)
+
def testConnectOldStyleEmitVoidSignal(self):
def callbackOne():
self.one_called += 1
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
-import unittest
from pathlib import Path
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from init_paths import init_test_paths
+from init_paths import init_test_paths # noqa: E402
init_test_paths(False)
"""
PYSIDE-2029: Tests that snake_case is isolated from imported modules
"""
-from PySide6.QtWidgets import QWidget
+from PySide6.QtWidgets import QWidget # noqa: E402
def test_no_snake_case():
print(__name__)
widget = QWidget()
- check = widget.sizeHint
+ check = widget.sizeHint # noqa
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
"""
is_pypy = hasattr(sys, "pypy_version_info")
-from PySide6.QtCore import QSize
-from PySide6.QtWidgets import QWidget, QSpinBox
+from PySide6.QtWidgets import QWidget
if not is_pypy:
- from __feature__ import snake_case
+ from __feature__ import snake_case # noqa
from helper.usesqapplication import UsesQApplication
import snake_case_sub
+
@unittest.skipIf(is_pypy, "__feature__ cannot yet be used with PyPy")
class SnakeCaseNoPropagateTest(UsesQApplication):
def testSnakeCase(self):
# this worked
widget = QWidget()
- check = widget.size_hint
+ check = widget.size_hint # noqa
snake_case_sub.test_no_snake_case()
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "testobject.h"
+#include "connection.h"
#include <QtCore/QDebug>
emit idValue(m_idValue);
}
+void TestObject::emitConnectionSignal(int handle)
+{
+ Connection connection(handle);
+ emit connectionSignal(connection);
+}
+
void TestObject::emitStaticMethodDoubleSignal()
{
emit staticMethodDouble();
using TypedefValue = IntValue;
+class Connection;
+
class PYSIDETEST_API TestObject : public QObject
{
Q_OBJECT
void addChild(QObject* c) { m_children.append(c); emit childrenChanged(m_children); }
void emitIdValueSignal();
+ void emitConnectionSignal(int handle);
void emitStaticMethodDoubleSignal();
void emitSignalWithDefaultValue_void();
signals:
void idValue(int newValue);
+ void connectionSignal(const Connection &c);
void justASignal();
void staticMethodDouble();
void childrenChanged(const QList<QObject*>&);
} // Namespace PySideCPP2
#endif // TESTOBJECT_H
-
};
#endif // TESTVIEW_H
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from PySide6.QtCore import QSize
from PySide6.QtWidgets import QWidget, QSpinBox
if not is_pypy:
- from __feature__ import true_property
+ from __feature__ import true_property # noqa
from helper.usesqapplication import UsesQApplication
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
<?xml version="1.0" encoding="UTF-8"?>
<typesystem package="testbinding">
<load-typesystem name="QtWidgets/typesystem_widgets.xml" generate="no"/>
+ <value-type name="Connection"/>
<value-type name="IntValue"/>
<primitive-type name="TypedefValue">
<!--
<namespace-type name="PySideCPP">
<object-type name="TestObjectWithNamespace"/>
<object-type name="TestObject2WithNamespace">
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
%PYARG_0 = Shiboken::String::fromCString("TestObject2WithNamespace(injected_repr)");
</inject-code>
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
if __name__ == '__main__':
unittest.main()
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
existence_test.py
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
init_platform.py
try:
from PySide6.QtCore import QLibraryInfo
result = QLibraryInfo.build()
- except:
+ except Exception:
pass
return result
set_ospaths(all_build_dir)
-import PySide6
+import PySide6 # noqa: E402
all_modules = list("PySide6." + _ for _ in PySide6.__all__)
# now we should be able to do all imports:
if not have_build_dir:
sys.path.insert(0, os.path.join(pyside_build_dir, "tests", "pysidetest"))
-import testbinding
+import testbinding # noqa: E402 F401
all_modules.append("testbinding")
-from shiboken6 import Shiboken
+from shiboken6 import Shiboken # noqa: E402 F401
all_modules.append("shiboken6.Shiboken")
-from shibokensupport.signature.lib.enum_sig import SimplifyingEnumerator
+from shibokensupport.signature.lib.enum_sig import SimplifyingEnumerator # noqa: E402
# Make sure not to get .pyc in Python2.
sourcepath = os.path.splitext(__file__)[0] + ".py"
-class Formatter(object):
+class Formatter:
"""
Formatter is formatting the signature listing of an enumerator.
print(*args, file=self.outfile, **kw) if self.outfile else None
@contextmanager
- def module(self, mod_name):
- self.print(f"")
+ def module(self, mod_name, *other):
+ self.print()
self.print(f"# Module {mod_name}")
- self.print(f"sig_dict.update({{")
+ self.print("sig_dict.update({")
yield
self.print(f' }}) if "{mod_name}" in sys.modules else None')
@contextmanager
- def klass(self, class_name, class_str):
+ def klass(self, class_name, class_str, *other):
self.print()
self.print(f"# class {self.mod_name}.{class_name}:")
yield
@contextmanager
- def function(self, func_name, signature):
+ def function(self, func_name, signature, *other):
if self.last_level > self.level:
self.print()
self.last_level = self.level
LICENSE_TEXT = """
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
fmt = Formatter(outfile)
enu = SimplifyingEnumerator(fmt)
lines = f.readlines()
- license_line = next((lno for lno, line in enumerate(lines)
+ license_line = next((lno for lno, line in enumerate(lines) # noqa: F841
if "$QT_END_LICENSE$" in line))
fmt.print("#recreate # uncomment this to enforce generation")
fmt.print(LICENSE_TEXT)
{build}
There are no default values, no variable names and no self
- parameter. Only types are present after simplification. The
- functions 'next' resp. '__next__' are removed to make the output
- identical for Python 2 and 3. '__div__' is also removed,
- since it exists in Python 2, only.
+ parameter. Only types are present after simplification.
"""
'''))
fmt.print("import sys")
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+from bs4 import BeautifulSoup
+from datetime import datetime
+from multiprocessing import Pool
+from textwrap import dedent
+import requests
+import os
+import time
+import re
+import json
+import argparse
"""
scrape_testresults.py
"""
-import sys
-if sys.version_info[:2] < (3, 6):
- print("This program is written for Python 3.6 or higher.")
- sys.exit(1)
-
DEMO_URL = ("https://testresults.qt.io/coin/api/results/pyside/pyside-setup/"
# The above URL part is fixed.
"30c1193ec56a86b8d0920c325185b9870f96941e/"
"MacOSMacOS_10_12x86_64MacOSMacOS_10_12x86_64Clangqtci-macos-"
- "10.12-x86_64-8-425364DebugAndRelease_Release/"
+ "10.12-x86_64-8-425364DebugAndRelease_Release/"
"d80c5d4547ea2b3d74188bd458955aae39cb32b4/"
"test_1535865484/"
"log.txt.gz")
-from bs4 import BeautifulSoup
-from datetime import datetime
-from multiprocessing import Pool
-from textwrap import dedent
-import requests
-import os
-import time
-import re
-import json
-import argparse
my_name = __file__ if __file__.endswith(".py") else __file__[:-1]
test_path = os.path.join(os.path.dirname(__file__), "testresults", "embedded")
stop_all = True
raise
startpos += len(prefix)
- text = text[startpos : startpos + 80]
+ text = text[startpos: startpos + 80]
ts = text[:19]
ts = re.sub(r'[^0-9]', '_', ts)
# check that it is a valid time stamp
offset = line.index("BEGIN_FILE")
if "END_FILE" in line:
stop = idx
- lines = lines[start : stop]
+ lines = lines[start: stop]
if offset:
lines = list(line[offset:] for line in lines)
# fix the lines - the original has no empty line after "# eof"
return # bad solution, but it stops fast
idx, n, url, level = idx_n_url_level
try:
- ret = handle_suburl(idx, n, url, level)
+ handle_suburl(idx, n, url, level)
return url, None
except requests.exceptions.RequestException as e:
return url, e
with open(cache_file, 'r') as fp:
known_urls = json.load(fp)
work_urls -= set(known_urls)
- level = 1
for sub_url in work_urls:
name = get_name(sub_url)
if name.endswith("/"):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
Supporting isolation of warnings
if mod:
reg = getattr(mod, warn_name, None)
if reg:
- # XXX We need to filter warnings for Python 2.
# This should be avoided by renaming the duplicate folders.
for k in reg:
if type(k) is tuple and re.match(ignore_re, k[0]):
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import gc
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for passing invalid callbacks to QObject.connect'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Connecting lambda to gui signals'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Connecting lambda to signals'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
from functools import partial
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
self.called = False
self.running = True
o = Sender()
- o.dummy2.connect(self.mySlot2, Qt.QueuedConnection)
+ o.dummy2.connect(self.mySlot2, Qt.ConnectionType.QueuedConnection)
o.callDummy2()
self.app.exec()
self.assertEqual(self.callCount, 1)
self.called = False
self.running = True
o = Sender()
- o.dummy2.connect(self.mySlot2, Qt.QueuedConnection)
+ o.dummy2.connect(self.mySlot2, Qt.ConnectionType.QueuedConnection)
o.callDummy2()
o.callDummy2()
self.app.exec()
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test case for QObject.receivers()'''
receiver2 = QObject()
self.assertEqual(sender.receivers(SIGNAL("")), 0)
sender.destroyed.connect(receiver1.deleteLater)
- self.assertEqual(sender.receivers(SIGNAL("destroyed()")), 1)
+ old_count = sender.receivers(SIGNAL("destroyed()"))
sender.destroyed.connect(receiver2.deleteLater)
- self.assertEqual(sender.receivers(SIGNAL("destroyed()")), 2)
+ self.assertEqual(sender.receivers(SIGNAL("destroyed()")), old_count + 1)
sender.disconnect(sender, SIGNAL("destroyed()"), receiver2, SLOT("deleteLater()"))
- self.assertEqual(sender.receivers(SIGNAL("destroyed()")), 1)
+ self.assertEqual(sender.receivers(SIGNAL("destroyed()")), old_count)
del receiver2
del receiver1
del sender
sender = QObject()
receiver = QObject()
sender.destroyed.connect(cute_slot)
- self.assertEqual(sender.receivers(SIGNAL("destroyed( )")), 1)
+ old_count = sender.receivers(SIGNAL("destroyed()"))
sender.destroyed.connect(receiver.deleteLater)
- self.assertEqual(sender.receivers(SIGNAL("destroyed()")), 2)
+ self.assertEqual(sender.receivers(SIGNAL("destroyed()")), old_count + 1)
del sender
del receiver
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for QObject.sender()'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Using self.connect(signal, method)'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test case for signal to signal connections.'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for PYSIDE-1354: Ensure that slots are invoked from the receiver's
thread context when using derived classes (and thus, a global receiver).'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
"""Connect signal using a Qt.ConnectionType as argument"""
obj1 = Sender()
- obj1.foo.connect(self.callback, Qt.DirectConnection)
+ obj1.foo.connect(self.callback, Qt.ConnectionType.DirectConnection)
self.args = tuple()
obj1.foo.emit(*self.args)
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
"""Tests covering signal emission and receiving to python slots"""
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
"""Tests covering signal emission and receiving to python slots"""
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QObject, Signal, SIGNAL, QProcess, QTimeLine
+from PySide6.QtCore import QObject, Signal, SIGNAL, QProcess, QTimeLine, Slot
from helper.usesqapplication import UsesQApplication
dummy_int = Signal(int)
+class Receiver(QObject):
+ '''Receiver class'''
+
+ def __init__(self, p=None):
+ super().__init__(p)
+ self.n = 0
+
+ @Slot(int)
+ def intSlot(self, n):
+ self.n = n
+
+
class PythonSignalToCppSlots(UsesQApplication):
'''Connect python signals to C++ slots'''
sender.dummy.emit()
new_dir = timeline.direction()
- if orig_dir == QTimeLine.Forward:
- self.assertEqual(new_dir, QTimeLine.Backward)
+ if orig_dir == QTimeLine.Direction.Forward:
+ self.assertEqual(new_dir, QTimeLine.Direction.Backward)
else:
- self.assertEqual(new_dir, QTimeLine.Forward)
+ self.assertEqual(new_dir, QTimeLine.Direction.Forward)
def testWithArgs(self):
'''Connect python signals to QTimeLine.setCurrentTime(int)'''
self.assertEqual(timeline.currentTime(), current + 42)
+class ConnectWithContext(UsesQApplication):
+ '''Test whether a connection with context QObject passes parameters.'''
+
+ def testIt(self):
+ sender = Sender()
+ receiver = Receiver()
+ context = sender
+ QObject.connect(sender, SIGNAL("dummy_int(int)"), context, receiver.intSlot)
+ sender.dummy_int.emit(42)
+ self.assertEqual(receiver.n, 42)
+
+
class CppSignalsToCppSlots(UsesQApplication):
'''Connection between C++ slots and signals'''
new_dir = timeline.direction()
- if orig_dir == QTimeLine.Forward:
- self.assertEqual(new_dir, QTimeLine.Backward)
+ if orig_dir == QTimeLine.Direction.Forward:
+ self.assertEqual(new_dir, QTimeLine.Direction.Backward)
else:
- self.assertEqual(new_dir, QTimeLine.Forward)
+ self.assertEqual(new_dir, QTimeLine.Direction.Forward)
called = False
self.arg = None
p = QProcess()
p.stateChanged.connect(self.slot)
- p.stateChanged.emit(QProcess.NotRunning)
- self.assertEqual(self.arg, QProcess.NotRunning)
+ p.stateChanged.emit(QProcess.ProcessState.NotRunning)
+ self.assertEqual(self.arg, QProcess.ProcessState.NotRunning)
if __name__ == '__main__':
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
from enum import Enum
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
super().__init__()
self.result = 0
- @Slot(Qt.Alignment, str)
+ @Slot(Qt.AlignmentFlag, str)
def handler(self, e, s):
print('handler', e, "type=", type(e).__name__, s)
self.result += 1
super().__init__()
def emit_test_sig(self):
- self.test_sig.emit(Qt.AlignLeft, "bla")
+ self.test_sig.emit(Qt.AlignmentFlag.AlignLeft, "bla")
class TestSignalNewEnum(unittest.TestCase):
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
def testConnectionType(self):
o = MyObject()
- o.timeout.connect(self.cb, type=Qt.DirectConnection)
+ o.timeout.connect(self.cb, type=Qt.ConnectionType.DirectConnection)
o.start(100)
self.app.exec()
self.assertTrue(self._cb_called)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for signal signature received by QObject::connectNotify().'''
sender = Obj()
receiver = QObject()
sender.connect(SIGNAL('destroyed()'), receiver, SLOT('deleteLater()'))
- # When connecting to a regular slot, and not a python callback function, QObject::connect
- # will use the non-cloned method signature, so connectinc to destroyed() will actually
- # connect to destroyed(QObject*).
self.assertEqual(sender.signal.methodSignature(), 'destroyed(QObject*)')
def testOldStyleWithPythonCallback(self):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Forced disconnection: Delete one end of the signal connection'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
"""Tests covering signal emission and receiving to python slots"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
# This is a small script printing out Qt binding class hierarchies
# for comparison purposes.
#
# meld pyside.list pyqt5.list
-import sys
-import pdb
from inspect import isclass
ignore = ["staticMetaObject",
if __name__ == '__main__':
- modules = [ 'QtCore',
- 'QtGui',
- 'QtHelp',
+ modules = ['QtCore',
+ 'QtGui',
+ 'QtHelp',
#'QtMultimedia',
- 'QtNetwork',
+ 'QtNetwork',
#'QtOpenGL',
- 'QtScript',
- 'QtScriptTools',
- 'QtSql',
- 'QtSvg',
- 'QtTest',
+ 'QtScript',
+ 'QtScriptTools',
+ 'QtSql',
+ 'QtSvg',
+ 'QtTest',
#'QtUiTools',
- 'QtXml',
- 'QtXmlPatterns' ]
+ 'QtXml',
+ 'QtXmlPatterns']
libraries = ["PySide6", "PyQt5"]
librarySymbols = {}
- for l in libraries:
+ for lib in libraries:
dictionary = []
- if l == "PyQt5":
+ if lib == "PyQt5":
import sip
sip.setapi('QDate', 2)
sip.setapi('QDateTime', 2)
sip.setapi('QVariant', 2)
for m in modules:
- exec(f"from {l} import {m}", globals(), locals())
+ exec(f"from {lib} import {m}", globals(), locals())
dictionary += recurse_into(m, eval(m))
- librarySymbols[l] = dictionary
+ librarySymbols[lib] = dictionary
print("PyQt5: ", len(librarySymbols["PyQt5"]), " PySide6: ", len(librarySymbols["PySide6"]))
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
Extensive manual test of pyside6-android-deploy
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import importlib
import os
# test config file contents
config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
- self.assertEqual(config_obj.get_value("app", "input_file"), "main.py")
+ self.assertTrue(config_obj.get_value("app", "input_file").endswith("main.py"))
self.assertEqual(config_obj.get_value("python", "android_packages"),
"buildozer==1.5.0,cython==0.29.33")
self.assertEqual(config_obj.get_value("android", "wheel_pyside"),
self.buildozer_config.unlink()
def test_errors(self, mock_extract_jar):
- # test if error raises for non existing NDK
- with self.assertRaises(FileNotFoundError) as context:
+ # test when no shiboken wheel is passed
+ with self.assertRaises(RuntimeError) as context:
+ self.android_deploy.main(name="android_app", pyside_wheel=self.pyside_wheel,
+ ndk_path=self.ndk_path, force=True)
+ self.assertTrue("Unable to find shiboken6 Android wheel" in str(context.exception))
+
+ # test when no PySide wheel is passed
+ with self.assertRaises(RuntimeError) as context:
self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
- pyside_wheel=self.pyside_wheel, force=True)
- self.assertTrue("Unable to find Android NDK" in str(context.exception))
+ ndk_path=self.ndk_path, force=True)
+ self.assertTrue("Unable to find PySide6 Android wheel" in str(context.exception))
+
+ # test if wheel name is correct
+ pyside_wheel_temp = Path("/tmp/"
+ "PySide6-6.8.0.1+commercial-6.8.0.1-cp311-cp311-android_fake.whl")
+ with self.assertRaises(RuntimeError) as context:
+ self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
+ pyside_wheel=pyside_wheel_temp, ndk_path=self.ndk_path,
+ force=True)
+ self.assertTrue("PySide wheel corrupted" in str(context.exception))
# test when cwd() is not project_dir
os.chdir(self.current_dir)
pyside_wheel=self.pyside_wheel, init=True, force=True)
self.assertTrue("For Android deployment to work" in str(context.exception))
+ @patch("deploy_lib.android.buildozer.BuildozerConfig._BuildozerConfig__find_jars")
+ @patch("deploy_lib.android.android_config.AndroidConfig.recipes_exist")
+ @patch("deploy_lib.android.android_config.AndroidConfig._find_dependent_qt_modules")
+ @patch("deploy_lib.android.android_config.find_qtlibs_in_wheel")
+ @patch("deploy_lib.android.android_config.download_android_ndk")
+ def test_no_ndk_passed(self, mock_ndk, mock_qtlibs, mock_extraqtmodules, mock_recipes_exist,
+ mock_find_jars, mock_extract_jar):
+ jar_dir = "tmp/jar/PySide6/jar"
+ mock_extract_jar.return_value = Path(jar_dir)
+ mock_qtlibs.return_value = self.pyside_wheel / "PySide6/Qt/lib"
+ mock_extraqtmodules.return_value = []
+ mock_recipes_exist.return_value = True
+ jars, init_classes = ["/tmp/jar/PySide6/jar/Qt6Android.jar",
+ "/tmp/jar/PySide6/jar/Qt6AndroidBindings.jar"], []
+ mock_find_jars.return_value = jars, init_classes
+ mock_ndk.return_value = Path("/tmp/android_ndk")
+
+ self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
+ pyside_wheel=self.pyside_wheel,
+ init=True, force=True,
+ keep_deployment_files=True)
+
+ # test config file contents
+ config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
+ self.assertEqual(config_obj.get_value("buildozer", "sdk_path"), '')
+ self.assertEqual(config_obj.get_value("buildozer", "ndk_path"), "/tmp/android_ndk")
+
@patch("deploy_lib.config.run_qmlimportscanner")
@patch("deploy_lib.android.android_config.extract_and_copy_jar")
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
Extensive manual test of pyside6-deploy
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import unittest
import tempfile
sys.path.append(os.fspath(Path(__file__).resolve().parents[2]))
from init_paths import init_test_paths, _get_qt_lib_dir # noqa: E402
+
init_test_paths(False)
cls.deploy_lib = importlib.import_module("deploy_lib")
cls.deploy = importlib.import_module("deploy")
sys.modules["deploy"] = cls.deploy
- files_to_ignore = [".cpp.o", ".qsb", ".webp"]
+ files_to_ignore = [".cpp.o", ".qsb"]
cls.dlls_ignore_nuitka = " ".join([f"--noinclude-dlls=*{file}"
for file in files_to_ignore])
self.deployment_files = self.temp_example_widgets / "deployment"
# All the plugins included. This is different from plugins_nuitka, because Nuitka bundles
# some plugins by default
- self.all_plugins = ["accessiblebridge", "egldeviceintegrations", "generic", "iconengines",
+ self.all_plugins = ["egldeviceintegrations", "generic", "iconengines",
"imageformats", "platforminputcontexts", "platforms",
- "platforms/darwin", "platformthemes", "styles", "xcbglintegrations"]
+ "platformthemes", "styles", "xcbglintegrations"]
# Plugins that needs to be passed to Nuitka
- plugins_nuitka = ("accessiblebridge,platforminputcontexts,platforms/darwin")
+ plugins_nuitka = ("platforminputcontexts")
self.expected_run_cmd = (
f"{sys.executable} -m nuitka {str(self.main_file)} --follow-imports"
f" --enable-plugin=pyside6 --output-dir={str(self.deployment_files)} --quiet"
# # test config file contents
config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
- self.assertEqual(config_obj.get_value("app", "input_file"), "tetrix.py")
- self.assertEqual(config_obj.get_value("app", "project_dir"), ".")
+ self.assertTrue(config_obj.get_value("app", "input_file").endswith("tetrix.py"))
+ self.assertTrue(config_obj.get_value("app", "project_dir").endswith("tetrix"))
self.assertEqual(config_obj.get_value("app", "exec_directory"), ".")
self.assertEqual(config_obj.get_value("python", "packages"),
- "Nuitka==2.3.2")
+ "Nuitka==2.5.1")
self.assertEqual(config_obj.get_value("qt", "qml_files"), "")
equ_base = "--quiet --noinclude-qt-translations"
equ_value = equ_base + " --static-libpython=no" if is_pyenv_python() else equ_base
self.deploy.main(main_file=fake_main_file, config_file=self.config_file)
self.assertTrue("Directory does not contain main.py file." in str(context.exception))
+ def testStandaloneMode(self, mock_plugins):
+ mock_plugins.return_value = self.all_plugins
+ # remove --onefile from self.expected_run_cmd and replace it with --standalone
+ self.expected_run_cmd = self.expected_run_cmd.replace(" --onefile", " --standalone")
+ # test standalone mode
+ original_output = self.deploy.main(self.main_file, mode="standalone", dry_run=True,
+ force=True)
+
+ self.assertEqual(original_output, self.expected_run_cmd)
+
+ @patch("deploy_lib.dependency_util.QtDependencyReader.get_qt_libs_dir")
+ def testExtraModules(self, mock_sitepackages, mock_plugins):
+ mock_sitepackages.return_value = Path(_get_qt_lib_dir())
+ mock_plugins.return_value = self.all_plugins
+ init_result = self.deploy.main(self.main_file, extra_modules_grouped="QtNetwork,QtOpenGL",
+ init=True, force=True)
+ self.assertEqual(init_result, None)
+ self.deploy.main(config_file=self.config_file, dry_run=True, force=True)
+
+ # test config file contents
+ config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
+ expected_modules = {"Core", "Gui", "Widgets", "Network", "OpenGL"}
+ if sys.platform != "win32":
+ expected_modules.add("DBus")
+ obtained_modules = set(config_obj.get_value("qt", "modules").split(","))
+ self.assertEqual(obtained_modules, expected_modules)
+ self.config_file.unlink()
+
+ @patch("deploy_lib.dependency_util.QtDependencyReader.get_qt_libs_dir")
+ def testExtraIgnoreDirs(self, mock_sitepackages, mock_plugins):
+ # create a directory to ignore
+ ignore_dir = self.temp_example_widgets / "ignore_dir"
+ ignore_dir.mkdir()
+ ignore_file = ignore_dir / "test_ignore.py"
+ ignore_file.write_text("from PySide6 import QtNetwork")
+
+ # rename the .pyproject file inside the example directory
+ project_file = self.temp_example_widgets / "tetrix.pyproject"
+ project_file.rename(self.temp_example_widgets / "tetrix.pyproject.bak")
+
+ mock_sitepackages.return_value = Path(_get_qt_lib_dir())
+ mock_plugins.return_value = self.all_plugins
+ init_result = self.deploy.main(self.main_file, extra_ignore_dirs="ignore_dir",
+ init=True, force=True)
+ self.assertEqual(init_result, None)
+ self.deploy.main(config_file=self.config_file, dry_run=True, force=True)
+
+ config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
+ expected_modules = {"Core", "Gui", "Widgets"}
+ if sys.platform != "win32":
+ expected_modules.add("DBus")
+ obtained_modules = set(config_obj.get_value("qt", "modules").split(","))
+ self.assertEqual(obtained_modules, expected_modules)
+ self.config_file.unlink()
+
+ #undo rename of project file
+ project_file = self.temp_example_widgets / "tetrix.pyproject.bak"
+ project_file.rename(self.temp_example_widgets / "tetrix.pyproject")
+
@unittest.skipIf(sys.platform == "darwin" and int(platform.mac_ver()[0].split('.')[0]) <= 11,
"Test only works on macOS version 12+")
# All the plugins included. This is different from plugins_nuitka, because Nuitka bundles
# some plugins by default
- self.all_plugins = ["accessiblebridge", "egldeviceintegrations", "generic", "iconengines",
- "imageformats", "networkaccess", "networkinformation",
- "platforminputcontexts", "platforms", "platforms/darwin",
- "platformthemes", "qmltooling", "scenegraph", "tls",
+ self.all_plugins = ["egldeviceintegrations", "generic", "iconengines",
+ "imageformats", "networkinformation",
+ "platforminputcontexts", "platforms",
+ "platformthemes", "qmltooling", "tls",
"xcbglintegrations"]
# Plugins that needs to be passed to Nuitka
- plugins_nuitka = ("accessiblebridge,networkaccess,networkinformation,platforminputcontexts,"
- "platforms/darwin,qml,qmltooling,scenegraph")
+ plugins_nuitka = ("networkinformation,platforminputcontexts,qml,qmltooling")
self.expected_run_cmd = (
f"{sys.executable} -m nuitka {str(self.main_file)} --follow-imports"
f" --enable-plugin=pyside6 --output-dir={str(self.deployment_files)} --quiet"
# test config file contents
config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
- self.assertEqual(config_obj.get_value("app", "input_file"), "main.py")
- self.assertEqual(config_obj.get_value("app", "project_dir"), ".")
+ self.assertTrue(config_obj.get_value("app", "input_file").endswith("main.py"))
+ self.assertTrue(config_obj.get_value("app", "project_dir").endswith("editingmodel"))
self.assertEqual(config_obj.get_value("app", "exec_directory"), ".")
self.assertEqual(config_obj.get_value("python", "packages"),
- "Nuitka==2.3.2")
+ "Nuitka==2.5.1")
self.assertEqual(config_obj.get_value("qt", "qml_files"), "main.qml,MovingRectangle.qml")
equ_base = "--quiet --noinclude-qt-translations"
equ_value = equ_base + " --static-libpython=no" if is_pyenv_python() else equ_base
config_obj.get_value("qt", "excluded_qml_plugins"),
"QtCharts,QtQuick3D,QtSensors,QtTest,QtWebEngine",
)
- expected_modules = {"Core", "Gui", "Qml", "Quick", "Network", "OpenGL", "QmlModels"}
+ expected_modules = {"Core", "Gui", "Qml", "Quick", "Network", "OpenGL", "QmlModels",
+ "QmlWorkerScript", "QmlMeta"}
if sys.platform != "win32":
expected_modules.add("DBus")
obtained_modules = set(config_obj.get_value("qt", "modules").split(","))
@patch("deploy_lib.dependency_util.QtDependencyReader.get_qt_libs_dir")
def testWebEngineQuickDryRun(self, mock_sitepackages, mock_plugins):
mock_sitepackages.return_value = Path(_get_qt_lib_dir())
- all_plugins = ["accessiblebridge", "egldeviceintegrations", "generic", "iconengines",
- "imageformats", "networkaccess", "networkinformation",
- "platforminputcontexts", "platforms", "platforms/darwin",
- "platformthemes", "qmltooling", "scenegraph", "tls",
+ all_plugins = ["egldeviceintegrations", "generic", "iconengines",
+ "imageformats", "networkinformation",
+ "platforminputcontexts", "platforms",
+ "platformthemes", "qmltooling", "tls",
"xcbglintegrations"]
mock_plugins.return_value = all_plugins
# this test case retains the QtWebEngine dlls
main_file = self.temp_example_webenginequick / "quicknanobrowser.py"
deployment_files = self.temp_example_webenginequick / "deployment"
# Plugins that needs to be passed to Nuitka
- plugins_nuitka = ("accessiblebridge,networkaccess,networkinformation,platforminputcontexts,"
- "platforms/darwin,qml,qmltooling,scenegraph")
+ plugins_nuitka = ("networkinformation,platforminputcontexts,qml,qmltooling")
qml_files = [
"ApplicationRoot.qml",
"BrowserDialog.qml",
# test config file contents
config_obj = self.deploy_lib.BaseConfig(config_file=config_file)
- self.assertEqual(config_obj.get_value("app", "input_file"), "quicknanobrowser.py")
+ self.assertTrue(config_obj.get_value("app", "input_file").endswith("quicknanobrowser.py"))
self.assertEqual(config_obj.get_value("qt", "qml_files"), ",".join(qml_files))
self.assertEqual(
config_obj.get_value("qt", "excluded_qml_plugins"),
"QtCharts,QtQuick3D,QtSensors,QtTest",
)
expected_modules = {"Core", "Gui", "Quick", "Qml", "WebEngineQuick", "Network", "OpenGL",
- "Positioning", "WebEngineCore", "WebChannel", "WebChannelQuick",
- "QmlModels"}
+ "QmlModels", "QmlWorkerScript", "QmlMeta", "WebEngineCore",
+ "Positioning", "WebChannelQuick", "WebChannel"}
if sys.platform != "win32":
expected_modules.add("DBus")
obtained_modules = set(config_obj.get_value("qt", "modules").split(","))
self.assertEqual(obtained_modules, expected_modules)
+@unittest.skipIf(sys.platform != "win32", "Test only works on Windows")
+class TestLongCommand(DeployTestBase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ example_qml = cls.example_root / "qml" / "editingmodel"
+ cls.temp_example_qml = Path(
+ shutil.copytree(example_qml, Path(cls.temp_dir) / "editingmodel")
+ ).resolve()
+
+ def setUp(self):
+ os.chdir(self.temp_example_qml)
+ self.main_file = self.temp_example_qml / "main.py"
+
+ @patch('deploy_lib.nuitka_helper.os.remove')
+ @patch("deploy_lib.config.run_qmlimportscanner")
+ @patch('deploy.DesktopConfig.qml_files', new_callable=mock.PropertyMock)
+ def test_main_with_mocked_qml_files(self, mock_qml_files, mock_qmlimportscanner, mock_remove):
+ mock_qmlimportscanner.return_value = ["QtQuick"]
+ mock_qml_files.return_value = [self.temp_example_qml / "MovingRectangle.qml"
+ for _ in range(500)]
+
+ command_str = self.deploy.main(self.main_file, force=True, keep_deployment_files=True,
+ dry_run=True)
+ mock_remove.assert_called_once()
+
+ # check if command_str ends with deploy_main.py
+ self.assertTrue(command_str.endswith("deploy_main.py"))
+
+ # check if deploy_main.py starts with # nuitka-project:
+ with open(self.temp_example_qml / "deploy_main.py", "r") as file:
+ # check if 516 lines start with # nuitka-project:
+ self.assertEqual(len([line for line in file.readlines()
+ if line.startswith("# nuitka-project:")]), 516)
+
+
+@unittest.skipIf(sys.platform == "darwin" and int(platform.mac_ver()[0].split('.')[0]) <= 11,
+ "Test only works on macOS version 12+")
+@patch("deploy_lib.config.QtDependencyReader.find_plugin_dependencies")
+class EmptyDSProjectTest(DeployTestBase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+
+ # setup a test DS Python project
+ base_path = Path(cls.temp_dir) / "PythonProject"
+
+ project_name = "TestProject"
+ files = [
+ base_path / "Python" / "autogen" / "settings.py",
+ base_path / "Python" / "autogen" / "resources.py",
+ base_path / "Python" / "main.py",
+ base_path / project_name / "test.qml",
+ base_path / f"{project_name}Content" / "test.qml",
+ base_path / f"{project_name}.qmlproject",
+ base_path / f"{project_name}.qmlproject.qtds",
+ base_path / f"{project_name}.qrc"
+ ]
+
+ # Create the files
+ for file in files:
+ file.parent.mkdir(parents=True, exist_ok=True)
+ file.touch(exist_ok=True)
+
+ cls.temp_example = base_path
+
+ def setUp(self):
+ os.chdir(self.temp_example)
+ self.temp_example = self.temp_example.resolve()
+ self.main_file = self.temp_example / "Python" / "main.py"
+ self.deployment_files = self.temp_example / "Python" / "deployment"
+
+ self.expected_run_cmd = (
+ f"{sys.executable} -m nuitka {self.main_file} --follow-imports"
+ f" --enable-plugin=pyside6 --output-dir={self.deployment_files} --quiet"
+ f" --noinclude-qt-translations"
+ f" {self.dlls_ignore_nuitka}"
+ f" --noinclude-dlls=*/qml/QtQuickEffectMaker/*"
+ f" --include-qt-plugins=qml"
+ )
+
+ if sys.platform != "win32":
+ self.expected_run_cmd += (
+ " --noinclude-dlls=libQt6Charts* --noinclude-dlls=libQt6Quick*"
+ " --noinclude-dlls=libQt6Quick3D* --noinclude-dlls=libQt6Sensors*"
+ " --noinclude-dlls=libQt6Test* --noinclude-dlls=libQt6WebEngine*"
+ )
+ else:
+ self.expected_run_cmd += (
+ " --noinclude-dlls=Qt6Charts* --noinclude-dlls=Qt6Quick*"
+ " --noinclude-dlls=Qt6Quick3D* --noinclude-dlls=Qt6Sensors*"
+ " --noinclude-dlls=Qt6Test* --noinclude-dlls=Qt6WebEngine*"
+ )
+
+ if sys.platform.startswith("linux"):
+ self.expected_run_cmd += f" --linux-icon={str(self.linux_icon)} --onefile"
+ elif sys.platform == "darwin":
+ self.expected_run_cmd += (f" --macos-app-icon={str(self.macos_icon)}"
+ " --macos-create-app-bundle --standalone")
+ elif sys.platform == "win32":
+ self.expected_run_cmd += f" --windows-icon-from-ico={str(self.win_icon)} --onefile"
+
+ if is_pyenv_python():
+ self.expected_run_cmd += " --static-libpython=no"
+
+ self.config_file = self.temp_example / "Python" / "pysidedeploy.spec"
+
+ def testDryRun(self, mock_plugins):
+ with patch("deploy_lib.config.run_qmlimportscanner") as mock_qmlimportscanner: # noqa: F841
+ original_output = self.deploy.main(self.main_file, dry_run=True, force=True)
+ self.assertEqual(self.expected_run_cmd, original_output)
+
+ @patch("deploy_lib.dependency_util.QtDependencyReader.get_qt_libs_dir")
+ def testConfigFile(self, mock_sitepackages, mock_plugins):
+ mock_sitepackages.return_value = Path(_get_qt_lib_dir())
+ # create config file
+ with patch("deploy_lib.config.run_qmlimportscanner") as mock_qmlimportscanner: # noqa: F841
+ init_result = self.deploy.main(self.main_file, init=True, force=True)
+ self.assertEqual(init_result, None)
+
+ # test config file contents
+ config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
+
+ self.assertTrue(config_obj.get_value("app", "input_file").endswith("main.py"))
+ self.assertTrue(config_obj.get_value("app", "project_dir").endswith("PythonProject"))
+ self.config_file.unlink()
+
+
if __name__ == "__main__":
unittest.main()
--- /dev/null
+PYSIDE_TEST(test_pyside6_project.py)
--- /dev/null
+// prop: json-converted
+// prop: auto-generated
+
+import QmlProject
+
+Project {
+ mainFile: "DrumpadContent/App.qml"
+ mainUiFile: "DrumpadContent/MainScreen.ui.qml"
+ enableCMakeGeneration: false
+ enablePythonGeneration: false
+ widgetApp: true
+ importPaths: [ "." ]
+
+ qdsVersion: "4.5"
+ quickVersion: "6.7"
+ qt6Project: true
+ qtForMCUs: false
+
+ multilanguageSupport: true
+ primaryLanguage: "en"
+ supportedLanguages: [ "en" ]
+
+ Environment {
+ QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT: "1"
+ QT_AUTO_SCREEN_SCALE_FACTOR: "1"
+ QT_ENABLE_HIGHDPI_SCALING: "0"
+ QT_LOGGING_RULES: "qt.qml.connections=false"
+ QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf"
+ }
+
+ QmlFiles {
+ directory: "Drumpad"
+ }
+
+ QmlFiles {
+ directory: "DrumpadContent"
+ }
+
+ QmlFiles {
+ directory: "Generated"
+ }
+
+ Files {
+ files: [
+ "qtquickcontrols2.conf"
+ ]
+ }
+
+ Files {
+ directory: "Drumpad"
+ filter: "qmldir"
+ }
+
+ Files {
+ directory: "DrumpadContent"
+ filter: "*.ttf;*.otf"
+ }
+}
--- /dev/null
+
+<RCC>
+ <qresource>
+ <file>Drumpad.qmlproject</file>
+ <file>Drumpad/TestComponent.qml</file>
+ <file>DrumpadContent/App.qml</file>
+ <file>DrumpadContent/MainScreen.ui.qml</file>
+ <file>qtquickcontrols2.conf</file>
+ <file>Resources/resource1.txt</file>
+ <file>Resources/resource2.txt</file>
+ </qresource>
+</RCC>
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Text {
+ text: "The quick brown fox"
+ color: "#303030"
+ font.family: "Ubuntu"
+ font.pixelSize: 28
+}
--- /dev/null
+module Drumpad
+
+TestComponent 1.0 TestComponent.qml
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import Drumpad
+
+Window {
+ id: root
+
+ height: 800
+ title: "Drumpad"
+ visible: true
+ width: 1200
+
+ MainScreen {
+ id: mainScreen
+
+ anchors.fill: parent
+ }
+}
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+/*
+This is a UI file (.ui.qml) that is intended to be edited in Qt Design Studio only.
+It is supposed to be strictly declarative and only uses a subset of QML. If you edit
+this file manually, you might introduce QML code that is not supported by Qt Design Studio.
+Check out https://doc.qt.io/qtcreator/creator-quick-ui-forms.html for details on .ui.qml files.
+*/
+import QtQuick
+import QtQuick.Controls
+import Drumpad
+
+Rectangle {
+ id: root
+
+ TestComponent {
+ }
+}
--- /dev/null
+module DrumpadContent
+App 1.0 App.qml
+MainScreen 1.0 MainScreen.ui.qml
--- /dev/null
+{
+ "files": ["main.py", "autogen/settings.py"]
+}
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+# This file is automatically generated by Qt Design Studio.
+
+import os
+import sys
+from pathlib import Path
+
+from PySide6.QtQml import QQmlApplicationEngine
+
+project_root = Path(__file__).parent.parent.parent
+
+
+def setup_qt_environment(qml_engine: QQmlApplicationEngine):
+ """
+ Load the QML application. Import the compiled resources when the application is deployed.
+ """
+ qml_app_url = "DrumpadContent/App.qml"
+
+ if "__compiled__" in globals():
+ # Application has been deployed using pyside6-deploy
+ try:
+ import autogen.resources # noqa: F401
+ except ImportError:
+ resource_file = Path(__file__).parent / "resources.py"
+ print(
+ f"Error: No compiled resources found in {resource_file.absolute()}\n"
+ f"Please compile the resources using pyside6-rcc or pyside6-project build",
+ file=sys.stderr,
+ )
+ sys.exit(1)
+
+ qml_engine.addImportPath(":/")
+ qml_engine.load(f":/{qml_app_url}")
+ return
+
+ qml_engine.addImportPath(str(project_root.absolute()))
+ os.environ["QT_QUICK_CONTROLS_CONF"] = str(project_root / "qtquickcontrols2.conf")
+ qml_engine.load(str(project_root / qml_app_url))
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import sys
+
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQml import QQmlApplicationEngine
+
+from autogen.settings import setup_qt_environment
+
+
+def main():
+ app = QGuiApplication(sys.argv)
+ engine = QQmlApplicationEngine()
+
+ setup_qt_environment(engine)
+
+ if not engine.rootObjects():
+ sys.exit(-1)
+
+ ex = app.exec()
+ del engine
+ return ex
+
+
+if __name__ == "__main__":
+ sys.exit(main())
--- /dev/null
+Resource 1
--- /dev/null
+Resource 2
--- /dev/null
+; This file can be edited to change the style of the application
+; Read "Qt Quick Controls 2 Configuration File" for details:
+; http://doc.qt.io/qt-5/qtquickcontrols2-configuration.html
+
+[Controls]
+Style=Basic
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+import contextlib
+import io
+import os
+import shutil
+import sys
+import unittest
+from unittest import mock
+from unittest import TestCase
+import tempfile
+import importlib
+from pathlib import Path
+
+sys.path.append(str(Path(__file__).resolve().parents[2]))
+from init_paths import init_test_paths
+
+init_test_paths(False)
+
+
+class PySide6ProjectTestBase(TestCase):
+ @classmethod
+ def setUpClass(cls):
+ cls.pyside_root = Path(__file__).parents[5].resolve()
+ cls.example_root = cls.pyside_root / "examples"
+ tools_path = cls.pyside_root / "sources" / "pyside-tools"
+ if tools_path not in sys.path:
+ sys.path.append(str(tools_path))
+ cls.project = importlib.import_module("project")
+ cls.project_lib = importlib.import_module("project_lib")
+ cls.temp_dir = Path(tempfile.mkdtemp())
+ cls.current_dir = Path.cwd()
+ # print no outputs to stdout
+ sys.stdout = mock.MagicMock()
+
+ @classmethod
+ def tearDownClass(cls):
+ os.chdir(cls.current_dir)
+ shutil.rmtree(cls.temp_dir)
+
+ def setUp(self):
+ os.chdir(self.temp_dir)
+
+
+class TestPySide6ProjectDesignStudio(PySide6ProjectTestBase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ example_drumpad = Path(__file__).parent / "example_drumpad"
+ cls.temp_example_drumpad = Path(
+ shutil.copytree(example_drumpad, cls.temp_dir / "drumpad")
+ ).resolve()
+
+ def testDrumpadExample(self):
+ # This test compiles the .qrc file into a .py file and checks whether the compilation is
+ # carried out only when required
+ compiled_resources_path = self.temp_example_drumpad / "Python" / "autogen" / "resources.py"
+ resources_path = self.temp_example_drumpad / "Drumpad.qrc"
+ requires_rebuild = self.project_lib.utils.requires_rebuild
+
+ self.assertFalse(compiled_resources_path.exists())
+
+ os.chdir(self.temp_example_drumpad / "Python")
+ self.project.main(mode="build")
+
+ self.assertTrue(compiled_resources_path.exists())
+ self.assertFalse(requires_rebuild([resources_path], compiled_resources_path))
+
+ # Refresh the modification timestamp of the .qrc resources file so that it is considered
+ # as modified
+ resources_path.touch()
+
+ self.assertTrue(requires_rebuild([resources_path], compiled_resources_path))
+
+ self.project.main(mode="build")
+
+ self.assertFalse(requires_rebuild([resources_path], compiled_resources_path))
+
+ # Refresh the modification timestamp of one of the resources files
+ list((self.temp_example_drumpad / "Resources").glob("*.txt"))[0].touch()
+ self.assertTrue(requires_rebuild([resources_path], compiled_resources_path))
+
+ self.project.main(mode="clean")
+
+ self.assertFalse(compiled_resources_path.exists())
+
+
+class TestPySide6ProjectNew(PySide6ProjectTestBase):
+ def testNewUi(self):
+ with self.assertRaises(SystemExit) as context:
+ self.project.main(mode="new-ui", file="TestProject")
+ test_project_path = Path("TestProject")
+ self.assertTrue((test_project_path / "TestProject.pyproject").exists())
+ self.assertTrue((test_project_path / "mainwindow.ui").exists())
+ self.assertTrue((test_project_path / "main.py").exists())
+ self.assertEqual(context.exception.code, 0)
+ shutil.rmtree(test_project_path)
+
+ def testRaiseErrorOnExistingProject(self):
+ with self.assertRaises(SystemExit) as context:
+ self.project.main(mode="new-ui", file="TestProject")
+ self.assertEqual(context.exception.code, 0)
+ error_message = io.StringIO()
+ with self.assertRaises(SystemExit) as context, contextlib.redirect_stderr(error_message):
+ self.project.main(mode="new-ui", file="TestProject")
+ self.assertEqual(context.exception.code, -1)
+ self.assertTrue(error_message.getvalue()) # some error message is printed
+ shutil.rmtree(self.temp_dir / "TestProject")
+
+ def testNewQuick(self):
+ with self.assertRaises(SystemExit) as context:
+ self.project.main(mode="new-quick", file="TestProject")
+ test_project_path = Path("TestProject")
+ self.assertTrue((test_project_path / "TestProject.pyproject").exists())
+ self.assertTrue((test_project_path / "main.qml").exists())
+ self.assertTrue((test_project_path / "main.py").exists())
+ self.assertEqual(context.exception.code, 0)
+ shutil.rmtree(test_project_path)
+
+ def testNewWidget(self):
+ with self.assertRaises(SystemExit) as context:
+ self.project.main(mode="new-widget", file="TestProject")
+ test_project_path = Path("TestProject")
+ self.assertTrue((test_project_path / "TestProject.pyproject").exists())
+ self.assertTrue((test_project_path / "main.py").exists())
+ self.assertEqual(context.exception.code, 0)
+ shutil.rmtree(test_project_path)
+
+ def testRaiseErrorWhenNoProjectNameIsSpecified(self):
+ error_message = io.StringIO()
+ with self.assertRaises(SystemExit) as context, contextlib.redirect_stderr(error_message):
+ self.project.main(mode="new-widget", file="")
+ self.assertEqual(context.exception.code, 1)
+ self.assertTrue(error_message.getvalue()) # some error message is printed
+
+
+class TestPySide6ProjectRun(PySide6ProjectTestBase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ example_widgets = cls.example_root / "widgets" / "widgets" / "tetrix"
+ cls.temp_example_tetrix = Path(
+ shutil.copytree(example_widgets, Path(cls.temp_dir) / "tetrix")
+ ).resolve()
+
+ def testRunEmptyProject(self):
+ project_folder = self.temp_dir / "TestProject"
+ project_folder.mkdir()
+ os.chdir(project_folder)
+ error_message = io.StringIO()
+ with self.assertRaises(SystemExit) as context, contextlib.redirect_stderr(error_message):
+ self.project.main(mode="run")
+ self.assertEqual(context.exception.code, 1)
+ self.assertTrue(error_message.getvalue()) # some error message is printed
+
+
+if __name__ == "__main__":
+ unittest.main()
# Copyright (C) 2018 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""Test for pyside6-qml
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Function to print a colored line to terminal'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
-class BasicPySlotCase(object):
+class BasicPySlotCase:
'''Base class that tests python slots and signal emissions.
Python slots are defined as any callable passed to QObject.connect().
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Helper metaclass do 'decorate' docstrings from base test case classes'''
if __name__ == '__main__':
# tests
- class BaseTest(object):
+ class BaseTest:
__metaclass__ = DocModifier
def testBase(self):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Helper classes and functions'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Helper classes and functions'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Helper classes and functions'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Helper classes and functions'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import http.server as BaseHTTPServer
import os
import select
import sys
import socketserver as SocketServer
-import tempfile
import threading
sys.path.append(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "util"))
def do_GET(self):
self.send_head()
- self.wfile.write(py3k.b(TestHandler.DATA))
+ self.wfile.write(TestHandler.DATA)
def do_HEAD(self):
self.send_head()
try:
handler = self.marshall_handler()
handler.do_request(self)
- except:
+ except: # noqa: E722
self.send_response(401)
self.send_header("WWW-Authenticate", "Basic realm='Secure Area'")
self.send_header("Content-type", "text/plain")
if self.isPy25:
try:
request, client_address = self.get_request()
- except socket.error:
+ except socket.error: # noqa: F821
return
if self.verify_request(request, client_address):
try:
self.process_request(request, client_address)
- except:
+ except: # noqa: E722
self.handle_error(request, client_address)
self.close_request(request)
try:
self.httpd = CompatTCPServer(('', self._port), handle)
break
- except:
+ except: # noqa: E722
self._port = self._port + random.randint(1, 100)
def port(self):
def shutdown(self):
self.httpd.shutdown()
self.join()
-
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import time
return repr(self.msg)
-class ProcessTimer(object):
+class ProcessTimer:
'''Timeout function for controlling a subprocess.Popen instance.
Naive implementation using busy loop, see later other means
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'Tests for processtimer.py'
Memcheck:Value4
fun:SHA1_Update
}
-
-
set(shiboken_MAJOR_VERSION "6")
-set(shiboken_MINOR_VERSION "7")
-set(shiboken_MICRO_VERSION "2")
+set(shiboken_MINOR_VERSION "8")
+set(shiboken_MICRO_VERSION "2.1")
set(shiboken_PRE_RELEASE_VERSION_TYPE "")
set(shiboken_PRE_RELEASE_VERSION "")
Luciano Wolf <luciano.wolf@openbossa.org>
Marcelo Lira <marcelo.lira@openbossa.org>
Renato Araujo Oliveira Filho <renato.filho@openbossa.org>
-
Luciano Wolf <luciano.wolf@openbossa.org>
Marcelo Lira <marcelo.lira@openbossa.org>
Renato Araujo Oliveira Filho <renato.filho@openbossa.org>
-
debughelpers_p.h
dependency.h
documentation.cpp documentation.h
+documentation_enums.h
dotview.cpp dotview.h
enclosingclassmixin.cpp enclosingclassmixin.h
enumtypeentry.h
return str;
}
-static void applyCachedFunctionModifications(AbstractMetaFunction *metaFunction,
+static void applyCachedFunctionModifications(const AbstractMetaFunctionPtr &metaFunction,
const FunctionModificationList &functionMods)
{
for (const FunctionModification &mod : functionMods) {
return;
}
- AbstractMetaFunction *metaFunction = traverseFunction(item, baseoperandClass);
+ auto metaFunction = traverseFunction(item, baseoperandClass);
if (metaFunction == nullptr)
return;
}
metaFunction->setFlags(flags);
metaFunction->setAccess(Access::Public);
- AbstractMetaClass::addFunction(baseoperandClass, AbstractMetaFunctionCPtr(metaFunction));
+ AbstractMetaClass::addFunction(baseoperandClass, metaFunction);
if (!metaFunction->arguments().isEmpty()) {
const auto include = metaFunction->arguments().constFirst().type().typeEntry()->include();
baseoperandClass->typeEntry()->addArgumentInclude(include);
if (streamedClass == nullptr)
return false;
- AbstractMetaFunction *streamFunction = traverseFunction(item, streamedClass);
+ auto streamFunction = traverseFunction(item, streamedClass);
if (!streamFunction)
return false;
funcClass = streamClass;
}
- AbstractMetaClass::addFunction(funcClass, AbstractMetaFunctionCPtr(streamFunction));
+ AbstractMetaClass::addFunction(funcClass, streamFunction);
auto funcTe = funcClass->typeEntry();
if (funcClass == streamClass)
funcTe->addArgumentInclude(streamedClass->typeEntry()->include());
+ clang::languageLevelOption(level));
}
FileModelItem result = clang::parse(arguments, addCompilerSupportArguments,
- clangFlags, builder)
+ level, clangFlags, builder)
? builder.dom() : FileModelItem();
const clang::BaseVisitor::Diagnostics &diagnostics = builder.diagnostics();
if (const auto diagnosticsCount = diagnostics.size()) {
if (!funcEntry || !funcEntry->generateCode())
continue;
- AbstractMetaFunction *metaFunc = traverseFunction(func, nullptr);
- if (!metaFunc)
+ auto metaFuncPtr = traverseFunction(func, nullptr);
+ if (!metaFuncPtr)
continue;
- AbstractMetaFunctionCPtr metaFuncPtr(metaFunc);
- if (!funcEntry->hasSignature(metaFunc->minimalSignature()))
+ if (!funcEntry->hasSignature(metaFuncPtr->minimalSignature()))
continue;
- metaFunc->setTypeEntry(funcEntry);
- applyFunctionModifications(metaFunc);
- metaFunc->applyTypeModifications();
+ metaFuncPtr->setTypeEntry(funcEntry);
+ applyFunctionModifications(metaFuncPtr);
+ metaFuncPtr->applyTypeModifications();
setInclude(funcEntry, func->fileName());
}
}
-void AbstractMetaBuilderPrivate::fixReturnTypeOfConversionOperator(AbstractMetaFunction *metaFunction)
+void AbstractMetaBuilderPrivate::fixReturnTypeOfConversionOperator(const AbstractMetaFunctionPtr &metaFunction)
{
if (!metaFunction->isConversionOperator())
return;
metaFunction->setType(metaType);
}
-AbstractMetaFunctionRawPtrList
+AbstractMetaFunctionList
AbstractMetaBuilderPrivate::classFunctionList(const ScopeModelItem &scopeItem,
AbstractMetaClass::Attributes *constructorAttributes,
const AbstractMetaClassPtr ¤tClass)
{
*constructorAttributes = {};
- AbstractMetaFunctionRawPtrList result;
+ AbstractMetaFunctionList result;
const FunctionList &scopeFunctionList = scopeItem->functions();
result.reserve(scopeFunctionList.size());
const bool isNamespace = currentClass->isNamespace();
} else if (function->isSpaceshipOperator() && !function->isDeleted()) {
if (currentClass)
AbstractMetaClass::addSynthesizedComparisonOperators(currentClass);
- } else if (auto *metaFunction = traverseFunction(function, currentClass)) {
+ } else if (auto metaFunction = traverseFunction(function, currentClass)) {
result.append(metaFunction);
} else if (!function->isDeleted() && function->functionType() == CodeModel::Constructor) {
auto arguments = function->arguments();
const AbstractMetaClassPtr &metaClass)
{
AbstractMetaClass::Attributes constructorAttributes;
- const AbstractMetaFunctionRawPtrList functions =
+ const AbstractMetaFunctionList functions =
classFunctionList(scopeItem, &constructorAttributes, metaClass);
metaClass->setAttributes(metaClass->attributes() | constructorAttributes);
- for (AbstractMetaFunction *metaFunction : functions) {
+ for (const auto &metaFunction : functions) {
if (metaClass->isNamespace())
metaFunction->setCppAttribute(FunctionAttribute::Static);
if (!metaFunction->isDestructor()
&& !(metaFunction->isPrivate() && metaFunction->functionType() == AbstractMetaFunction::ConstructorFunction)) {
- if (metaFunction->isSignal() && metaClass->hasSignal(metaFunction))
- qCWarning(lcShiboken, "%s", qPrintable(msgSignalOverloaded(metaClass, metaFunction)));
+ if (metaFunction->isSignal() && metaClass->hasSignal(metaFunction.get()))
+ qCWarning(lcShiboken, "%s", qPrintable(msgSignalOverloaded(metaClass,
+ metaFunction.get())));
if (metaFunction->isConversionOperator())
fixReturnTypeOfConversionOperator(metaFunction);
- AbstractMetaClass::addFunction(metaClass, AbstractMetaFunctionCPtr(metaFunction));
+ AbstractMetaClass::addFunction(metaClass, metaFunction);
applyFunctionModifications(metaFunction);
} else if (metaFunction->isDestructor()) {
metaClass->setHasPrivateDestructor(metaFunction->isPrivate());
metaClass->setHasProtectedDestructor(metaFunction->isProtected());
metaClass->setHasVirtualDestructor(metaFunction->isVirtual());
}
- if (!metaFunction->ownerClass()) {
- delete metaFunction;
- metaFunction = nullptr;
- }
}
fillAddedFunctions(metaClass);
return result;
}
-void AbstractMetaBuilderPrivate::applyFunctionModifications(AbstractMetaFunction *func)
+void AbstractMetaBuilderPrivate::applyFunctionModifications(const AbstractMetaFunctionPtr &func)
{
AbstractMetaFunction& funcRef = *func;
for (const FunctionModification &mod : func->modifications(func->implementingClass())) {
bool AbstractMetaBuilderPrivate::traverseAddedGlobalFunction(const AddedFunctionPtr &addedFunc,
QString *errorMessage)
{
- AbstractMetaFunction *metaFunction =
- traverseAddedFunctionHelper(addedFunc, nullptr, errorMessage);
+ auto metaFunction = traverseAddedFunctionHelper(addedFunc, nullptr, errorMessage);
if (metaFunction == nullptr)
return false;
- m_globalFunctions << AbstractMetaFunctionCPtr(metaFunction);
+ m_globalFunctions << metaFunction;
return true;
}
-AbstractMetaFunction *
+AbstractMetaFunctionPtr
AbstractMetaBuilderPrivate::traverseAddedFunctionHelper(const AddedFunctionPtr &addedFunc,
const AbstractMetaClassPtr &metaClass /* = {} */,
QString *errorMessage)
msgAddedFunctionInvalidReturnType(addedFunc->name(),
addedFunc->returnType().qualifiedName(),
*errorMessage, metaClass);
- return nullptr;
+ return {};
}
- auto *metaFunction = new AbstractMetaFunction(addedFunc);
+ auto metaFunction = std::make_shared<AbstractMetaFunction>(addedFunc);
metaFunction->setType(returnType.value());
metaFunction->setFunctionType(functionTypeFromName(addedFunc->name()));
msgAddedFunctionInvalidArgType(addedFunc->name(),
arg.typeInfo.qualifiedName(), i + 1,
*errorMessage, metaClass);
- delete metaFunction;
- return nullptr;
+ return {};
}
type->decideUsagePattern();
const AbstractMetaClassPtr &metaClass,
QString *errorMessage)
{
- AbstractMetaFunction *metaFunction =
- traverseAddedFunctionHelper(addedFunc, metaClass, errorMessage);
+ auto metaFunction = traverseAddedFunctionHelper(addedFunc, metaClass, errorMessage);
if (metaFunction == nullptr)
return false;
metaFunction->setDeclaringClass(metaClass);
metaFunction->setImplementingClass(metaClass);
- AbstractMetaClass::addFunction(metaClass, AbstractMetaFunctionCPtr(metaFunction));
+ AbstractMetaClass::addFunction(metaClass, metaFunction);
metaClass->setHasNonPrivateConstructor(true);
return true;
}
-void AbstractMetaBuilderPrivate::fixArgumentNames(AbstractMetaFunction *func, const FunctionModificationList &mods)
+void AbstractMetaBuilderPrivate::fixArgumentNames(const AbstractMetaFunctionPtr &func,
+ const FunctionModificationList &mods)
{
AbstractMetaArgumentList &arguments = func->arguments();
// Apply the <array> modifications of the arguments
static bool applyArrayArgumentModifications(const FunctionModificationList &functionMods,
- AbstractMetaFunction *func,
+ const AbstractMetaFunctionPtr &func,
QString *errorMessage)
{
for (const FunctionModification &mod : functionMods) {
m_rejectedFunctions.insert({reason, signatureWithType, sortKey, rejectReason});
}
-AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const FunctionModelItem &functionItem,
- const AbstractMetaClassPtr ¤tClass)
+// Check for special Qt argument types which should be ignored.
+enum class QtSpecialArgument
+{
+ None,
+ PrivateSignal,
+ Disambiguated // by QT6_DECL_NEW_OVERLOAD_TAIL
+};
+
+static QtSpecialArgument qtSpecialArgument(CodeModel::FunctionType functionType,
+ const ArgumentList &arguments)
+{
+ if (arguments.isEmpty())
+ return QtSpecialArgument::None;
+ const QStringList &lastArgumentType = arguments.constLast()->type().qualifiedName();
+ if (functionType == CodeModel::Signal && lastArgumentType.constLast() == "QPrivateSignal"_L1)
+ return QtSpecialArgument::PrivateSignal;
+ if (lastArgumentType.size() == 2 && lastArgumentType.constFirst() == "Qt"_L1
+ && lastArgumentType.constLast() == "Disambiguated_t"_L1) {
+ return QtSpecialArgument::Disambiguated;
+ }
+ return QtSpecialArgument::None;
+}
+
+AbstractMetaFunctionPtr
+ AbstractMetaBuilderPrivate::traverseFunction(const FunctionModelItem &functionItem,
+ const AbstractMetaClassPtr ¤tClass)
{
const auto *tdb = TypeDatabase::instance();
if (!functionItem->templateParameters().isEmpty())
- return nullptr;
+ return {};
if (functionItem->isDeleted()) {
switch (functionItem->functionType()) {
default:
break;
}
- return nullptr;
+ return {};
}
const QString &functionName = functionItem->name();
const QString className = currentClass != nullptr ?
// Skip enum helpers generated by Q_ENUM
if ((currentClass == nullptr || currentClass->isNamespace())
&& (functionName == u"qt_getEnumMetaObject" || functionName == u"qt_getEnumName")) {
- return nullptr;
+ return {};
}
// Clang: Skip qt_metacast(), qt_metacall(), expanded from Q_OBJECT
if (currentClass != nullptr) {
if (functionName == u"qt_check_for_QGADGET_macro"
|| functionName.startsWith(u"qt_meta")) {
- return nullptr;
+ return {};
}
if (functionName == u"metaObject" && className != u"QObject")
- return nullptr;
+ return {};
}
} // PySide extensions
if (tdb->isFunctionRejected(className, functionName, &rejectReason)) {
rejectFunction(functionItem, currentClass,
AbstractMetaBuilder::GenerationDisabled, rejectReason);
- return nullptr;
+ return {};
}
const QString &signature = functionSignature(functionItem);
qCInfo(lcShiboken, "%s::%s was rejected by the type database (%s).",
qPrintable(className), qPrintable(signature), qPrintable(rejectReason));
}
- return nullptr;
+ return {};
}
if (functionItem->isFriend())
- return nullptr;
+ return {};
const auto cppAttributes = functionItem->attributes();
const bool deprecated = cppAttributes.testFlag(FunctionAttribute::Deprecated);
if (deprecated && m_skipDeprecated) {
rejectFunction(functionItem, currentClass,
AbstractMetaBuilder::GenerationDisabled, u" is deprecated."_s);
- return nullptr;
+ return {};
}
AbstractMetaFunction::Flags flags;
- auto *metaFunction = new AbstractMetaFunction(functionName);
+ auto metaFunction = std::make_shared<AbstractMetaFunction>(functionName);
metaFunction->setCppAttributes(cppAttributes);
const QByteArray cSignature = signature.toUtf8();
const QString unresolvedSignature =
if (tdb->isReturnTypeRejected(className, returnType.toString(), &rejectReason)) {
rejectFunction(functionItem, currentClass,
AbstractMetaBuilder::GenerationDisabled, rejectReason);
- delete metaFunction;
- return nullptr;
+ return {};
}
TranslateTypeFlags flags;
qPrintable(msgSkippingFunction(functionItem, signature, reason)));
rejectFunction(functionItem, currentClass,
AbstractMetaBuilder::UnmatchedReturnType, reason);
- delete metaFunction;
- return nullptr;
+ return {};
}
metaFunction->setType(type.value());
}
ArgumentList arguments = functionItem->arguments();
- // Add private signals for documentation purposes
- if (!arguments.isEmpty()
- && m_apiExtractorFlags.testFlag(ApiExtractorFlag::UsePySideExtensions)
- && functionItem->functionType() == CodeModel::Signal
- && arguments.constLast()->type().qualifiedName().constLast() == u"QPrivateSignal") {
- flags.setFlag(AbstractMetaFunction::Flag::PrivateSignal);
- arguments.removeLast();
+ if (m_apiExtractorFlags.testFlag(ApiExtractorFlag::UsePySideExtensions)) {
+ switch (qtSpecialArgument(functionItem->functionType(), arguments)) {
+ case QtSpecialArgument::None:
+ break;
+ case QtSpecialArgument::PrivateSignal:
+ flags.setFlag(AbstractMetaFunction::Flag::PrivateSignal);
+ arguments.removeLast(); // Add private signals for documentation purposes
+ break;
+ case QtSpecialArgument::Disambiguated:
+ arguments.removeLast(); // Strip QT6_DECL_NEW_OVERLOAD_TAIL
+ if (!currentClass || currentClass->typeEntry()->generateCode()) {
+ const QString signature = qualifiedFunctionSignatureWithType(functionItem, className);
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgStrippingQtDisambiguatedArgument(functionItem, signature)));
+ }
+ break;
+ }
}
if (arguments.size() == 1) {
if (tdb->isArgumentTypeRejected(className, arg->type().toString(), &rejectReason)) {
rejectFunction(functionItem, currentClass,
AbstractMetaBuilder::GenerationDisabled, rejectReason);
- delete metaFunction;
- return nullptr;
+ return {};
}
TranslateTypeFlags flags;
qPrintable(msgSkippingFunction(functionItem, signature, reason)));
rejectFunction(functionItem, currentClass,
AbstractMetaBuilder::UnmatchedArgumentType, reason);
- delete metaFunction;
- return nullptr;
+ return {};
}
auto metaType = metaTypeO.value();
AbstractMetaArgumentList &metaArguments = metaFunction->arguments();
const FunctionModificationList functionMods = currentClass
- ? AbstractMetaFunction::findClassModifications(metaFunction, currentClass)
- : AbstractMetaFunction::findGlobalModifications(metaFunction);
+ ? AbstractMetaFunction::findClassModifications(metaFunction.get(), currentClass)
+ : AbstractMetaFunction::findGlobalModifications(metaFunction.get());
applyCachedFunctionModifications(metaFunction, functionMods);
&& metaFunction->argumentName(i + 1, false, currentClass).isEmpty()) {
qCWarning(lcShiboken, "%s",
qPrintable(msgUnnamedArgumentDefaultExpression(currentClass, i + 1,
- className, metaFunction)));
+ className, metaFunction.get())));
}
}
}
QString errorMessage;
- if (!applyArrayArgumentModifications(f->modifications(subclass), f.get(),
+ if (!applyArrayArgumentModifications(f->modifications(subclass), f,
&errorMessage)) {
qCWarning(lcShiboken, "While specializing %s (%s): %s",
qPrintable(subclass->name()), qPrintable(templateClass->name()),
if (spec.has_value()) {
spec->setIndex(i);
metaClass->addPropertySpec(spec.value());
- } else {
+ } else if (!errorMessage.isEmpty()) {
QString message;
QTextStream str(&message);
str << metaClass->sourceLocation() << errorMessage;
topologicalSortHelper(const QList<std::shared_ptr<MetaClass> > &classList,
const Dependencies &additionalDependencies)
{
- Graph<std::shared_ptr<MetaClass> > graph(classList.cbegin(), classList.cend());
+ Graph<std::shared_ptr<MetaClass> > graph(classList);
for (const auto &dep : additionalDependencies) {
if (!graph.addEdge(dep.parent, dep.child)) {
static bool isIntegerConstant(const QStringView expr)
{
- bool isNumber;
+ bool isNumber{};
auto n = expr.toInt(&isNumber, /* guess base: 0x or decimal */ 0);
Q_UNUSED(n);
return isNumber;
static bool isFloatConstant(const QStringView expr)
{
- bool isNumber;
+ bool isNumber{};
auto d = expr.toDouble(&isNumber);
Q_UNUSED(d);
return isNumber;
if (isIntegerConstant(expr))
return expr;
- const QString field = qualifyStaticField(klass, expr);
+ QString field = qualifyStaticField(klass, expr);
if (!field.isEmpty())
return field;
qualified.prepend(resolveEnumValueScopePrefix(metaEnum, token));
qualifiedTokens.append(qualified);
}
- const QString qualifiedExpression = qualifiedTokens.join(u" | "_s);
+ QString qualifiedExpression = qualifiedTokens.join(u" | "_s);
if (!typeCast)
return qualifiedExpression;
const QSet<QString> &enumsDeclarations);
void traverseEnums(const ScopeModelItem &item, const AbstractMetaClassPtr &parent,
const QStringList &enumsDeclarations);
- AbstractMetaFunctionRawPtrList classFunctionList(const ScopeModelItem &scopeItem,
- AbstractMetaClass::Attributes *constructorAttributes,
- const AbstractMetaClassPtr ¤tClass);
+ AbstractMetaFunctionList classFunctionList(const ScopeModelItem &scopeItem,
+ AbstractMetaClass::Attributes *constructorAttributes,
+ const AbstractMetaClassPtr ¤tClass);
void traverseFunctions(const ScopeModelItem& item,
const AbstractMetaClassPtr &parent);
- static void applyFunctionModifications(AbstractMetaFunction *func);
+ static void applyFunctionModifications(const AbstractMetaFunctionPtr &func);
void traverseFields(const ScopeModelItem &item, const AbstractMetaClassPtr &parent);
bool traverseStreamOperator(const FunctionModelItem &functionItem,
const AbstractMetaClassPtr ¤tClass);
void traverseOperatorFunction(const FunctionModelItem &item,
const AbstractMetaClassPtr ¤tClass);
- AbstractMetaFunction *traverseAddedFunctionHelper(const AddedFunctionPtr &addedFunc,
- const AbstractMetaClassPtr &metaClass,
- QString *errorMessage);
+ AbstractMetaFunctionPtr
+ traverseAddedFunctionHelper(const AddedFunctionPtr &addedFunc,
+ const AbstractMetaClassPtr &metaClass,
+ QString *errorMessage);
bool traverseAddedGlobalFunction(const AddedFunctionPtr &addedFunc,
QString *errorMessage);
bool traverseAddedMemberFunction(const AddedFunctionPtr &addedFunc,
const AbstractMetaClassPtr ¤tClass,
AbstractMetaBuilder::RejectReason reason,
const QString &rejectReason);
- AbstractMetaFunction *traverseFunction(const FunctionModelItem &function,
- const AbstractMetaClassPtr ¤tClass);
+ AbstractMetaFunctionPtr
+ traverseFunction(const FunctionModelItem &function,
+ const AbstractMetaClassPtr ¤tClass);
std::optional<AbstractMetaField> traverseField(const VariableModelItem &field,
const AbstractMetaClassCPtr &cls);
void checkFunctionModifications() const;
* said class.
* \param metaFunction conversion operator function to be fixed.
*/
- static void fixReturnTypeOfConversionOperator(AbstractMetaFunction *metaFunction);
+ static void fixReturnTypeOfConversionOperator(const AbstractMetaFunctionPtr &metaFunction);
void parseQ_Properties(const AbstractMetaClassPtr &metaClass,
const QStringList &declarations);
void sortLists();
void setInclude(const TypeEntryPtr &te, const QString &path) const;
- static void fixArgumentNames(AbstractMetaFunction *func, const FunctionModificationList &mods);
+ static void fixArgumentNames(const AbstractMetaFunctionPtr &func,
+ const FunctionModificationList &mods);
void fillAddedFunctions(const AbstractMetaClassPtr &metaClass);
AbstractMetaClassCPtr resolveTypeSystemTypeDef(const AbstractMetaType &t) const;
{
if (isAnonymous())
return findMatchingEnumValue(d->m_enumValues, value);
- const int sepPos = value.indexOf(u"::");
+ const auto sepPos = value.indexOf(u"::");
if (sepPos == -1)
return findMatchingEnumValue(d->m_enumValues, value);
if (name() == value.left(sepPos))
#include <QtCore/QDebug>
+#include <algorithm>
+
using namespace Qt::StringLiterals;
class AbstractMetaFieldData : public QSharedData
/*******************************************************************************
* Indicates that this field has a modification that removes it
*/
+static bool isRemoved(const FieldModification &mod) { return mod.isRemoved(); }
+
bool AbstractMetaField::isModifiedRemoved() const
{
const FieldModificationList &mods = modifications();
- for (const FieldModification &mod : mods) {
- if (mod.isRemoved())
- return true;
- }
-
- return false;
+ return std::any_of(mods.cbegin(), mods.cend(), isRemoved);
}
+static bool isOpaqueContainer(const FieldModification &mod) { return mod.isOpaqueContainer(); }
+
bool AbstractMetaField::generateOpaqueContainer() const
{
const FieldModificationList &mods = modifications();
- for (const FieldModification &mod : mods) {
- if (mod.isOpaqueContainer())
- return true;
- }
- return false;
+ return std::any_of(mods.cbegin(), mods.cend(), isOpaqueContainer);
}
const AbstractMetaType &AbstractMetaField::type() const
/*******************************************************************************
* Indicates that this function has a modification that removes it
*/
+
+static bool fmIsRemoved(const FunctionModification &mod) { return mod.isRemoved(); }
+
bool AbstractMetaFunction::isModifiedRemoved(AbstractMetaClassCPtr cls) const
{
if (!isInGlobalScope() && !cls)
cls = d->m_implementingClass;
- for (const auto &mod : modifications(cls)) {
- if (mod.isRemoved())
- return true;
- }
+ const auto &mods = modifications(cls);
+ return std::any_of(mods.cbegin(), mods.cend(), fmIsRemoved);
+}
- return false;
+static bool isFinal(const FunctionModification &mod)
+{
+ return mod.modifiers().testFlag(FunctionModification::Final);
}
bool AbstractMetaFunction::isModifiedFinal(AbstractMetaClassCPtr cls) const
{
if (!isInGlobalScope() && cls == nullptr)
cls = d->m_implementingClass;
- for (const auto &mod : modifications(cls)) {
- if (mod.modifiers().testFlag(FunctionModification::Final))
- return true;
- }
- return false;
+ const auto &mods = modifications(cls);
+ return std::any_of(mods.cbegin(), mods.cend(), isFinal);
}
bool AbstractMetaFunction::isVoid() const
}
// Match "const Foo &getFoo() const" / "Foo &getFoo()" / "Foo getFoo() const"
- const auto otherType = other->type();
+ const auto &otherType = other->type();
if (d->m_type.name() != otherType.name()
|| d->m_type.indirectionsV() != otherType.indirectionsV()) {
return false;
return cpy;
}
+static bool usesRValueReference(const AbstractMetaArgument &a)
+{
+ return a.type().referenceType() == RValueReference;
+}
+
bool AbstractMetaFunction::usesRValueReferences() const
{
- if (d->m_functionType == MoveConstructorFunction || d->m_functionType == MoveAssignmentOperatorFunction)
- return true;
- if (d->m_type.referenceType() == RValueReference)
- return true;
- for (const AbstractMetaArgument &a : d->m_arguments) {
- if (a.type().referenceType() == RValueReference)
- return true;
- }
- return false;
+ return d->m_functionType == MoveConstructorFunction
+ || d->m_functionType == MoveAssignmentOperatorFunction
+ || d->m_type.referenceType() == RValueReference
+ || std::any_of(d->m_arguments.cbegin(), d->m_arguments.cend(), usesRValueReference);
}
bool AbstractMetaFunction::generateBinding() const
m_cachedSignature += u", "_s;
m_cachedSignature += t.cppSignature();
// We need to have the argument names in the qdoc files
- m_cachedSignature += u' ';
+ if (!m_cachedSignature.endsWith(u'*') && !m_cachedSignature.endsWith(u'&'))
+ m_cachedSignature += u' ';
m_cachedSignature += a.name();
}
m_cachedSignature += u')';
return argumentMod.owner();
}
}
- return ArgumentOwner();
+ return {};
}
QString AbstractMetaFunction::conversionRule(TypeSystem::Language language, int key) const
}
}
- return QString();
+ return {};
}
bool AbstractMetaFunction::hasConversionRule(TypeSystem::Language language, int idx) const
d->m_arguments = arguments;
}
+void AbstractMetaFunction::setArgumentName(qsizetype a, const QString &name)
+{
+ d->m_arguments[a].setName(name);
+}
+
void AbstractMetaFunction::addArgument(const AbstractMetaArgument &argument)
{
d->m_arguments << argument;
&& !d->applyTypeModification(this, am.modifiedType(),
n, &errorMessage)) {
throw Exception(errorMessage);
- } else if (am.isRemoved() && n != 0) {
+ }
+ if (am.isRemoved() && n != 0) {
if (n < 1 || n > d->m_arguments.size()) {
errorMessage =
msgArgumentRemovalFailed(this, n,
d->m_modificationCache.clear();
}
-const DocModificationList AbstractMetaFunction::addedFunctionDocModifications() const
+DocModificationList AbstractMetaFunction::addedFunctionDocModifications() const
{
return d->m_addedFunction
? d->m_addedFunction->docModifications() : DocModificationList{};
QString AbstractMetaFunction::argumentName(int index,
bool /* create */,
- AbstractMetaClassCPtr /* implementor */) const
+ const AbstractMetaClassCPtr & /* implementor */) const
{
return d->m_arguments[--index].name();
}
return d->m_name == u"operator()";
}
+static bool isCodeInjection(const FunctionModification &mod)
+{
+ return mod.isCodeInjection();
+}
+
bool AbstractMetaFunction::hasInjectedCode() const
{
const FunctionModificationList &mods = modifications(ownerClass());
- for (const FunctionModification &mod : mods) {
- if (mod.isCodeInjection())
- return true;
- }
- return false;
+ return std::any_of(mods.cbegin(), mods.cend(), isCodeInjection);
}
// Traverse the code snippets, return true if predicate returns true
}, TypeSystem::CodeSnipPositionAny);
}
+static bool isModifiedToPrivate(const FunctionModification &mod)
+{
+ return mod.modifiers().testFlag(FunctionModification::Private);
+}
+
bool AbstractMetaFunction::isVisibilityModifiedToPrivate() const
{
- for (const auto &mod : modifications()) {
- if (mod.modifiers().testFlag(FunctionModification::Private))
- return true;
- }
- return false;
+ const auto &mods = modifications();
+ return std::any_of(mods.cbegin(), mods.cend(), isModifiedToPrivate);
}
struct ComparisonOperator
const AbstractMetaArgumentList &arguments() const;
AbstractMetaArgumentList &arguments();
void setArguments(const AbstractMetaArgumentList &arguments);
+ void setArgumentName(qsizetype a, const QString &name); // For documentation purposes
void addArgument(const AbstractMetaArgument &argument);
int actualMinimumArgumentCount() const;
// Return the argument index accounting for the isModifiedRemoved arguments [0..n-1]
const FunctionModificationList &modifications(AbstractMetaClassCPtr implementor = {}) const;
void clearModificationsCache();
- const DocModificationList addedFunctionDocModifications() const;
+ DocModificationList addedFunctionDocModifications() const;
static FunctionModificationList findClassModifications(const AbstractMetaFunction *f,
AbstractMetaClassCPtr implementor);
/**
* Return the argument name if there is a modification the renamed value will be returned
*/
- QString argumentName(int index, bool create = true, AbstractMetaClassCPtr cl = {}) const;
+ QString argumentName(int index, bool create = true,
+ const AbstractMetaClassCPtr &cl = {}) const;
int propertySpecIndex() const;
void setPropertySpecIndex(int i);
return attributes().testFlag(PropertyResetter);
}
-Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaFunction::CompareResult)
+Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaFunction::CompareResult);
Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaFunction::Attributes);
return returned;
}
+static bool isArithmeticOperatorOverload(const AbstractMetaFunctionCPtr &f)
+{
+ return f->ownerClass() == f->implementingClass()
+ && f->isArithmeticOperator() && !f->isPrivate();
+}
+
bool AbstractMetaClass::hasArithmeticOperatorOverload() const
{
- for (const auto & f: d->m_functions) {
- if (f->ownerClass() == f->implementingClass() && f->isArithmeticOperator() && !f->isPrivate())
- return true;
- }
- return false;
+ return std::any_of(d->m_functions.cbegin(), d->m_functions.cend(),
+ isArithmeticOperatorOverload);
+}
+
+static bool isIncDecrementOperatorOverload(const AbstractMetaFunctionCPtr &f)
+{
+ return f->ownerClass() == f->implementingClass()
+ && f->isIncDecrementOperator() && !f->isPrivate();
}
bool AbstractMetaClass::hasIncDecrementOperatorOverload() const
{
- for (const auto & f: d->m_functions) {
- if (f->ownerClass() == f->implementingClass()
- && f->isIncDecrementOperator() && !f->isPrivate()) {
- return true;
- }
- }
- return false;
+ return std::any_of(d->m_functions.cbegin(), d->m_functions.cend(),
+ isIncDecrementOperatorOverload);
+}
+
+static bool isBitwiseOperatorOverload(const AbstractMetaFunctionCPtr &f)
+{
+ return f->ownerClass() == f->implementingClass()
+ && f->isBitwiseOperator() && !f->isPrivate();
}
bool AbstractMetaClass::hasBitwiseOperatorOverload() const
{
- for (const auto & f: d->m_functions) {
- if (f->ownerClass() == f->implementingClass() && f->isBitwiseOperator() && !f->isPrivate())
- return true;
- }
- return false;
+ return std::any_of(d->m_functions.cbegin(), d->m_functions.cend(),
+ isBitwiseOperatorOverload);
+}
+
+static bool isComparisonOperatorOverload(const AbstractMetaFunctionCPtr &f)
+{
+ return f->ownerClass() == f->implementingClass()
+ && f->isComparisonOperator() && !f->isPrivate();
}
bool AbstractMetaClass::hasComparisonOperatorOverload() const
{
- for (const auto &f : d->m_functions) {
- if (f->ownerClass() == f->implementingClass() && f->isComparisonOperator() && !f->isPrivate())
- return true;
- }
- return false;
+ return std::any_of(d->m_functions.cbegin(), d->m_functions.cend(),
+ isComparisonOperatorOverload);
+}
+
+static bool isLogicalOperatorOverload(const AbstractMetaFunctionCPtr &f)
+{
+ return f->ownerClass() == f->implementingClass()
+ && f->isLogicalOperator() && !f->isPrivate();
}
bool AbstractMetaClass::hasLogicalOperatorOverload() const
{
- for (const auto &f : d->m_functions) {
- if (f->ownerClass() == f->implementingClass() && f->isLogicalOperator() && !f->isPrivate())
- return true;
- }
- return false;
+ return std::any_of(d->m_functions.cbegin(), d->m_functions.cend(),
+ isLogicalOperatorOverload);
}
const AbstractMetaFieldList &AbstractMetaClass::fields() const
static const char *operators[]
= {"operator==", "operator!=", "operator<", "operator<=", "operator>", "operator>="};
- for (auto *op : operators) {
+ for (const auto *op : operators) {
auto *f = AbstractMetaClassPrivate::createFunction(QLatin1StringView(op),
AbstractMetaFunction::ComparisonOperator,
Access::Public, arguments,
d->m_sourceLocation = sourceLocation;
}
-AbstractMetaClassCList allBaseClasses(const AbstractMetaClassCPtr metaClass)
+AbstractMetaClassCList allBaseClasses(const AbstractMetaClassCPtr &metaClass)
{
AbstractMetaClassCList result;
recurseClassHierarchy(metaClass, [&result] (const AbstractMetaClassCPtr &c) {
inline bool hasParentManagement(const AbstractMetaClassCPtr &c)
{ return bool(parentManagementEntry(c)); }
-AbstractMetaClassCList allBaseClasses(const AbstractMetaClassCPtr metaClass);
+AbstractMetaClassCList allBaseClasses(const AbstractMetaClassCPtr &metaClass);
Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractMetaClass::CppWrapper);
using AbstractMetaFieldList = QList<AbstractMetaField>;
using AbstractMetaFunctionRawPtrList = QList<AbstractMetaFunction *>;
using AbstractMetaFunctionCList = QList<AbstractMetaFunctionCPtr>;
+using AbstractMetaFunctionList = QList<AbstractMetaFunctionPtr>;
using AbstractMetaTypeList = QList<AbstractMetaType>;
using UsingMembers = QList<UsingMember>;
}
// Element type to be used for ArrayHandle<>, strip constness.
- auto elementType = new AbstractMetaType(*this);
+ auto *elementType = new AbstractMetaType(*this);
auto indir = indirectionsV();
indir.pop_front();
elementType->setIndirectionsV(indir);
if (m_pattern == AbstractMetaType::ArrayPattern) {
// Build nested array dimensions a[2][3] in correct order
result += m_arrayElementType->minimalSignature();
- const int arrayPos = result.indexOf(u'[');
+ const auto arrayPos = result.indexOf(u'[');
if (arrayPos != -1)
result.insert(arrayPos, formatArraySize(m_arrayElementCount));
else
}
if (!m_instantiations.isEmpty()) {
result += u'<';
- if (minimal)
- result += u' ';
const auto size = stripDefaultTemplateArgs(m_typeEntry, m_instantiations);
for (qsizetype i = 0; i < size; ++i) {
if (i > 0)
// Check defaultValue, "int @b@=5"
const auto equalPos = t.lastIndexOf(u'=');
if (equalPos != -1) {
- const int defaultValuePos = equalPos + 1;
+ const auto defaultValuePos = equalPos + 1;
argument.defaultValue =
t.mid(defaultValuePos, t.size() - defaultValuePos).trimmed().toString();
}
// Check @name@
const auto atPos = typeString.indexOf(u'@');
if (atPos != -1) {
- const int namePos = atPos + 1;
- const int nameEndPos = typeString.indexOf(u'@', namePos);
+ const auto namePos = atPos + 1;
+ const auto nameEndPos = typeString.indexOf(u'@', namePos);
if (nameEndPos == -1) {
if (errorMessage != nullptr) {
*errorMessage = u"Mismatched @ in \""_s
? callOperator.size() : 0;
const auto openParenPos = signature.indexOf(u'(', parenSearchStartPos);
if (openParenPos < 0) {
- return AddedFunctionPtr(new AddedFunction(signature.toString(),
- arguments, returnType));
+ return std::make_shared<AddedFunction>(signature.toString(),
+ arguments, returnType);
}
const QString name = signature.left(openParenPos).trimmed().toString();
{
return asv.visit([needle](auto s) { return asv_containsImpl(s, needle); });
}
+
+static qsizetype asv_indexOfImpl(QLatin1StringView v, const char *needle)
+{
+ return v.indexOf(QLatin1StringView(needle));
+}
+
+static qsizetype asv_indexOfImpl(QUtf8StringView v, const char *needle)
+{
+ const char *data = v.data();
+ const char *match = std::strstr(data, needle);
+ return match != nullptr ? qsizetype(match - data) : qsizetype(-1);
+}
+
+static qsizetype asv_indexOfImpl(QStringView v, const char *needle)
+{
+ return v.indexOf(QLatin1StringView(needle));
+}
+
+qsizetype asv_indexOf(QAnyStringView asv, const char *needle)
+{
+ return asv.visit([needle](auto s) { return asv_indexOfImpl(s, needle); });
+}
#ifndef ANYSTRINGVIEW_STREAM_H
#define ANYSTRINGVIEW_STREAM_H
+#include <QtCore/QtTypes>
#include <QtCore/QtClassHelperMacros>
QT_FORWARD_DECLARE_CLASS(QAnyStringView)
bool asv_contains(QAnyStringView asv, char needle);
bool asv_contains(QAnyStringView asv, const char *needle);
+qsizetype asv_indexOf(QAnyStringView asv, const char *needle);
#endif // ANYSTRINGVIEW_STREAM_H
#include "abstractmetalang.h"
#include "codesnip.h"
#include "exception.h"
+#include "graph.h"
#include "messages.h"
#include "modifications.h"
#include "optionsparser.h"
const QString &code,
bool toPythonMacro);
void addInstantiatedSmartPointer(InstantiationCollectContext &context,
- const AbstractMetaType &type);
+ const AbstractMetaType &type) const;
AbstractMetaBuilder *m_builder = nullptr;
};
return result;
}
+static qsizetype indexOfPointee(const InstantiatedSmartPointers &instantiatedList,
+ const AbstractMetaClassCPtr &pointee)
+{
+ for (qsizetype i = 0, size = instantiatedList.size(); i < size; ++i) {
+ if (instantiatedList.at(i).pointee == pointee)
+ return i;
+ }
+ return -1;
+}
+
+// Sort the list of instantiated smart pointers such that base classes go before
+// descendant classes since those register conversions to smart_ptr<base> for
+// which the base definition needs to exist (PYSIDE-2946).
+static InstantiatedSmartPointers
+ topologicalSortSmartPointers(const InstantiatedSmartPointers &instantiatedList)
+{
+ const auto size = instantiatedList.size();
+ if (size < 2)
+ return instantiatedList;
+
+ // Create a graph (using int indexes for sorting) with dependency edges for the inheritance
+ // within the pointee classes list.
+ Graph<InstantiatedSmartPointer> graph(instantiatedList);
+ for (qsizetype i = 0; i < size; ++i) {
+ const auto &smp = instantiatedList.at(i);
+ if (smp.pointee) {
+ for (const auto &base : smp.pointee->baseClasses()) {
+ const auto baseIndex = indexOfPointee(instantiatedList, base);
+ if (baseIndex != -1)
+ graph.addEdgeByIndexes(baseIndex, i);
+ }
+ }
+ }
+
+ const auto sortedGraphResult = graph.topologicalSort();
+ // Should not really fail since it is only by base classes ATM...
+ if (!sortedGraphResult.isValid())
+ throw Exception("Failed to sort the list of instantiated smart pointers (cyclic dependency?)"_L1);
+
+ return sortedGraphResult.result;
+}
+
static inline void classListToCList(const AbstractMetaClassList &list, AbstractMetaClassCList *target)
{
target->reserve(list.size());
result.m_flags = flags;
result.m_typedefTargetToName = d->m_builder->typedefTargetToName();
qSwap(result.m_instantiatedContainers, collectContext.instantiatedContainers);
- qSwap(result.m_instantiatedSmartPointers, collectContext.instantiatedSmartPointers);
+
+ // Populate the list of pointees by type entry.
+ for (auto &smp : collectContext.instantiatedSmartPointers) {
+ const auto instantiationTe = smp.type.instantiations().constFirst().typeEntry();
+ if (instantiationTe->isComplex())
+ smp.pointee = AbstractMetaClass::findClass(result.m_metaClasses, instantiationTe);
+ }
+ result.m_instantiatedSmartPointers =
+ topologicalSortSmartPointers(collectContext.instantiatedSmartPointers);
return result;
}
QString ApiExtractorPrivate::getSimplifiedContainerTypeName(const AbstractMetaType &type)
{
- const QString signature = type.cppSignature();
+ QString signature = type.cppSignature();
if (!type.typeEntry()->isContainer() && !type.typeEntry()->isSmartPointer())
return signature;
QString typeName = signature;
}
void ApiExtractorPrivate::addInstantiatedSmartPointer(InstantiationCollectContext &context,
- const AbstractMetaType &type)
+ const AbstractMetaType &type) const
{
InstantiatedSmartPointer smp;
smp.type = canonicalSmartPtrInstantiation(type);
QString name = ste->getTargetName(smp.type);
auto parentTypeEntry = ste->parent();
+ // FIXME PYSIDE 7: Make global scope the default behavior?
+ // Note: Also requires changing SmartPointerTypeEntry::getTargetName()
+ // to not strip namespaces from unnamed instances.
+ const bool globalScope = name.startsWith("::"_L1);
+ if (globalScope) {
+ name.remove(0, 2);
+ parentTypeEntry = typeSystemTypeEntry(ste);
+ }
+
auto colonPos = name.lastIndexOf(u"::");
const bool withinNameSpace = colonPos != -1;
if (withinNameSpace) { // user defined
{
addInstantiatedContainersAndSmartPointers(context, func->type(), func->signature());
for (const AbstractMetaArgument &arg : func->arguments()) {
- const auto argType = arg.type();
+ const auto &argType = arg.type();
const auto type = argType.viewOn() != nullptr ? *argType.viewOn() : argType;
addInstantiatedContainersAndSmartPointers(context, type, func->signature());
}
qsizetype start = 0;
QString errorMessage;
while ((start = code.indexOf(convMacro, start)) != -1) {
- int end = code.indexOf(u']', start);
+ const auto end = code.indexOf(u']', start);
start += offset;
if (code.at(start) != u'%') {
QString typeString = code.mid(start, end - start);
};
#endif // APIEXTRACTOR_H
-
{
AbstractMetaClassCPtr smartPointer; // Template class
AbstractMetaClassCPtr specialized; // Specialized for type
+ AbstractMetaClassCPtr pointee;
AbstractMetaType type;
};
#include <QtCore/QStack>
#include <QtCore/QList>
-#include <cstring>
-#include <ctype.h>
-
using namespace Qt::StringLiterals;
namespace clang {
{
const auto newLine = snippetV.find('\n'); // Multiline noexcept specifications have been found in Qt
const bool truncate = newLine != std::string::npos;
- const qsizetype length = qsizetype(truncate ? newLine : snippetV.size());
+ const auto length = qsizetype(truncate ? newLine : snippetV.size());
QString snippet = QString::fromUtf8(snippetV.data(), length);
if (truncate)
snippet += "..."_L1;
static QStringList qualifiedName(const QString &t)
{
QStringList result;
- int end = t.indexOf(u'<');
+ auto end = t.indexOf(u'<');
if (end == -1)
end = t.indexOf(u'(');
if (end == -1)
end = t.size();
- int lastPos = 0;
+ qsizetype lastPos = 0;
while (true) {
- const int nextPos = t.indexOf(u"::"_s, lastPos);
+ const auto nextPos = t.indexOf(u"::"_s, lastPos);
if (nextPos < 0 || nextPos >= end)
break;
result.append(t.mid(lastPos, nextPos - lastPos));
if (m_currentClass && typeName.startsWith(u"type-parameter-0-")) {
if (cacheable != nullptr)
*cacheable = false;
- bool ok;
+ bool ok{};
const int n = QStringView{typeName}.mid(17).toInt(&ok);
if (ok) {
auto currentTemplate = currentTemplateClass();
const std::string_view snippet = bv->getCodeSnippet(cursor);
auto equalSign = snippet.find('=');
if (equalSign == std::string::npos)
- return QString();
+ return {};
++equalSign;
QString result = QString::fromLocal8Bit(snippet.data() + equalSign,
qsizetype(snippet.size() - equalSign));
// "std::vector<T>").
const QStringList &baseScope = it.value()->scope();
if (!baseScope.isEmpty()) {
- const int lastSep = baseClassName.lastIndexOf(u"::"_s);
+ const auto lastSep = baseClassName.lastIndexOf(u"::"_s);
if (lastSep >= 0)
baseClassName.remove(0, lastSep + u"::"_s.size());
baseClassName.prepend(u"::"_s);
}
}
-Builder::Builder()
+Builder::Builder() :
+ d(new BuilderPrivate(this))
{
- d = new BuilderPrivate(this);
}
Builder::~Builder()
Q_DISABLE_COPY_MOVE(Builder)
Builder();
- ~Builder();
+ ~Builder() override;
void setForceProcessSystemIncludes(const QStringList &systemIncludes);
{
QDebugStateSaver saver(s);
s.nospace();
- CXFile file; // void *
- unsigned line;
- unsigned column;
- unsigned offset;
+ CXFile file{}; // void *
+ unsigned line{};
+ unsigned column{};
+ unsigned offset{};
clang_getExpansionLocation(location, &file, &line, &column, &offset);
const CXString cxFileName = clang_getFileName(file);
// Has been observed to be 0 for invalid locations
bool BaseVisitor::_handleVisitLocation(const CXSourceLocation &location)
{
- CXFile cxFile; // void *
- unsigned line;
- unsigned column;
- unsigned offset;
+ CXFile cxFile{}; // void *
+ unsigned line{};
+ unsigned column{};
+ unsigned offset{};
clang_getExpansionLocation(location, &cxFile, &line, &column, &offset);
if (cxFile == m_currentCxFile) // Same file?
static CXTranslationUnit createTranslationUnit(CXIndex index,
const QByteArrayList &args,
bool addCompilerSupportArguments,
+ LanguageLevel level,
unsigned flags = 0)
{
// courtesy qdoc
QByteArrayList clangArgs;
if (addCompilerSupportArguments) {
- clangArgs += emulatedCompilerOptions();
+ clangArgs += emulatedCompilerOptions(level);
clangArgs += defaultArgs;
}
clangArgs += detectVulkan();
QScopedArrayPointer<const char *> argv(byteArrayListToFlatArgV(clangArgs));
qDebug().noquote().nospace() << msgCreateTranslationUnit(clangArgs, flags);
- CXTranslationUnit tu;
+ CXTranslationUnit tu{};
CXErrorCode err = clang_parseTranslationUnit2(index, nullptr, argv.data(),
clangArgs.size(), nullptr, 0,
defaultFlags | flags, &tu);
*/
bool parse(const QByteArrayList &clangArgs, bool addCompilerSupportArguments,
- unsigned clangFlags, BaseVisitor &bv)
+ LanguageLevel level, unsigned clangFlags, BaseVisitor &bv)
{
CXIndex index = clang_createIndex(0 /* excludeDeclarationsFromPCH */,
1 /* displayDiagnostics */);
CXTranslationUnit translationUnit =
createTranslationUnit(index, clangArgs, addCompilerSupportArguments,
- clangFlags);
+ level, clangFlags);
if (!translationUnit)
return false;
#include <string_view>
#include <utility>
+enum class LanguageLevel;
+
namespace clang {
struct Diagnostic;
bool parse(const QByteArrayList &clangArgs,
bool addCompilerSupportArguments,
- unsigned clangFlags, BaseVisitor &ctx);
+ LanguageLevel level, unsigned clangFlags, BaseVisitor &ctx);
} // namespace clang
CXString getFileNameFromLocation(const CXSourceLocation &location)
{
- CXFile file;
- unsigned line;
- unsigned column;
- unsigned offset;
+ CXFile file{};
+ unsigned line{};
+ unsigned column{};
+ unsigned offset{};
clang_getExpansionLocation(location, &file, &line, &column, &offset);
return clang_getFileName(file);
}
}
// Returns clang options needed for emulating the host compiler
-QByteArrayList emulatedCompilerOptions()
+QByteArrayList emulatedCompilerOptions(LanguageLevel level)
{
QByteArrayList result;
HeaderPaths headerPaths;
switch (compiler()) {
case Compiler::Msvc:
result.append("-fms-compatibility-version="_ba + msvcCompatVersion());
- result.append(QByteArrayLiteral("-fdelayed-template-parsing"));
+ if (level < LanguageLevel::Cpp20)
+ result.append("-fdelayed-template-parsing"_ba);
result.append(QByteArrayLiteral("-Wno-microsoft-enum-value"));
result.append("/Zc:__cplusplus"_ba);
// Fix yvals_core.h: STL1000: Unexpected compiler version, expected Clang 7 or newer (MSVC2017 update)
namespace clang {
QVersionNumber libClangVersion();
-QByteArrayList emulatedCompilerOptions();
+QByteArrayList emulatedCompilerOptions(LanguageLevel level);
LanguageLevel emulatedCompilerLanguageLevel();
const char *languageLevelOption(LanguageLevel l);
}
break;
case WebXmlCodeTag::Parameter:
- Q_ASSERT(!cd->functions.isEmpty());
- cd->functions.last().parameters.append(attributes.value(u"type"_s).toString());
+ Q_ASSERT(!cd->functions.isEmpty()); {
+ FunctionDocumentation &last = cd->functions.last();
+ last.parameters.append(attributes.value(u"type"_s).toString());
+ last.parameterNames.append(attributes.value(u"name"_s).toString());
+ }
break;
case WebXmlCodeTag::Property: {
PropertyDocumentation pd;
return result;
}
-std::optional<ClassDocumentation> parseWebXml(const QString &fileName, QString *errorMessage)
+static bool parseWebXmlHelper(QFile *file, ClassDocumentation *result, QString *errorMessage)
{
- ClassDocumentation result;
-
- QFile file(fileName);
- if (!file.open(QIODevice::Text | QIODevice::ReadOnly)) {
- *errorMessage = msgCannotOpenForReading(file);
- return std::nullopt;
- }
-
WebXmlCodeTag lastTag = WebXmlCodeTag::Other;
- QXmlStreamReader reader(&file);
+ QXmlStreamReader reader(file);
while (!reader.atEnd()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement: {
const auto currentTag = tag(reader.name());
- parseWebXmlElement(currentTag, reader.attributes(), &result);
+ parseWebXmlElement(currentTag, reader.attributes(), result);
switch (currentTag) { // Store relevant tags in lastTag
case WebXmlCodeTag::Class:
case WebXmlCodeTag::Function:
QString *target = nullptr;
switch (lastTag) {
case WebXmlCodeTag::Class:
- target = &result.description;
+ target = &result->description;
break;
case WebXmlCodeTag::Function:
- target = &result.functions.last().description;
+ target = &result->functions.last().description;
break;
case WebXmlCodeTag::Enum:
- target = &result.enums.last().description;
+ target = &result->enums.last().description;
break;
case WebXmlCodeTag::Property:
- target = &result.properties.last().description;
+ target = &result->properties.last().description;
default:
break;
}
}
if (reader.error() != QXmlStreamReader::NoError) {
- *errorMessage= msgXmlError(fileName, reader);
- return std::nullopt;
+ *errorMessage= msgXmlError(file->fileName(), reader);
+ return false;
+ }
+
+ return result;
+}
+
+std::optional<ClassDocumentation> parseWebXml(const QStringList &fileNames, QString *errorMessage)
+{
+ ClassDocumentation result;
+ for (const auto &fileName : fileNames) {
+ QFile file(fileName);
+ if (!file.open(QIODevice::Text | QIODevice::ReadOnly)) {
+ *errorMessage = msgCannotOpenForReading(file);
+ return std::nullopt;
+ }
+ if (!parseWebXmlHelper(&file, &result, errorMessage))
+ return std::nullopt;
}
sortDocumentation(&result);
return result;
}
+// Helpers to remove some sections with information on how to build
+// and link and the C++ reference from the WebXML module description
+static void removeElement(QByteArrayView begin, QByteArrayView end,
+ QByteArray *data)
+{
+ auto startPos = data->indexOf(begin);
+ if (startPos != -1) {
+ auto endPos = data->indexOf(end, startPos + begin.size());
+ if (endPos != -1)
+ data->remove(startPos, endPos + end.size() - startPos);
+ }
+}
+
+static void removeSection(const QByteArray &id,
+ QByteArray *data)
+{
+ QByteArray contentBegin = "<contents name=\"" + id + '"';
+ removeElement(contentBegin, "/>"_ba, data);
+ QByteArray sectionBegin = "<section id=\""_ba + id + "\">"_ba;
+ removeElement(sectionBegin, "</section>"_ba, data);
+}
+
QString webXmlModuleDescription(const QString &fileName, QString *errorMessage)
{
QFile file(fileName);
return {};
}
+ QByteArray text = file.readAll();
+ file.close();
+ static const QByteArrayList cppSectionIds{
+ "api-reference"_ba, "building-with-cmake"_ba, "building-with-qmake"_ba,
+ "c-api"_ba, "c-classes"_ba, "examples"_ba, "qml-api"_ba, "reference"_ba,
+ "reference-and-examples"_ba, "using-the-module"_ba
+ };
+ for (const auto &cppSectionId : cppSectionIds)
+ removeSection(cppSectionId, &text);
+
+ QBuffer buffer(&text);
+ buffer.open(QIODevice::ReadOnly);
+
QString result;
- QXmlStreamReader reader(&file);
+ QXmlStreamReader reader(&buffer);
while (!reader.atEnd()) {
switch (reader.readNext()) {
case QXmlStreamReader::StartElement:
/// A function in a WebXML/doxygen document
struct FunctionDocumentation : public FunctionDocumentationQuery
{
+ QStringList parameterNames;
QString signature;
QString returnType;
QString description;
};
/// Parse a WebXML class/namespace document
-std::optional<ClassDocumentation> parseWebXml(const QString &fileName, QString *errorMessage);
+std::optional<ClassDocumentation> parseWebXml(const QStringList &fileNames, QString *errorMessage);
/// Extract the module description from a WebXML module document
QString webXmlModuleDescription(const QString &fileName, QString *errorMessage);
bool isValueTypeWithCopyConstructorOnly() const;
void setValueTypeWithCopyConstructorOnly(bool v);
+ QString docFile() const;
+ void setDocFile(const QString &docFile);
+
// FIXME PYSIDE 7: Remove this
static bool isParentManagementEnabled();
static void setParentManagementEnabled(bool e);
dbg << ')';
return dbg;
}
-
#include "abstractmetalang_typedefs.h"
#include "modifications_typedefs.h"
+#include "documentation.h"
#include <QtCore/QString>
class AbstractMetaClass;
class DocModification;
-class Documentation;
class XQuery;
struct FunctionDocumentation;
+struct ModuleDocumentation
+{
+ Documentation documentation;
+ QString qmlTypesUrl;
+};
+
class DocParser
{
public:
DocParser();
virtual ~DocParser();
- virtual void fillDocumentation(const AbstractMetaClassPtr &metaClass) = 0;
+ virtual QString fillDocumentation(const AbstractMetaClassPtr &metaClass) = 0;
virtual void fillGlobalFunctionDocumentation(const AbstractMetaFunctionPtr &f);
virtual void fillGlobalEnumDocumentation(AbstractMetaEnum &e);
- /**
- * Process and retrieves documentation concerning the entire
- * module or library.
- * \return object containing module/library documentation information
- */
- virtual Documentation retrieveModuleDocumentation() = 0;
-
void setDocumentationDataDirectory(const QString& dir)
{
m_docDataDir = dir;
return m_docDataDir;
}
- void setLibrarySourceDirectory(const QString& dir)
- {
- m_libSourceDir = dir;
- }
- /**
- * Informs the location of the library being parsed. The library
- * source code is parsed for the documentation comments.
- * \return the path for the directory containing the source code of
- * the library beign parsed.
- */
- QString librarySourceDirectory() const
- {
- return m_libSourceDir;
- }
-
- void setPackageName(const QString& packageName)
- {
- m_packageName = packageName;
- }
- /**
- * Retrieves the name of the package (or module or library) being parsed.
- * \return the name of the package (module/library) being parsed
- */
- QString packageName() const
- {
- return m_packageName;
- }
-
/**
* Process and retrieves documentation concerning the entire
* module or library.
* \return object containing module/library documentation information
* \todo Merge with retrieveModuleDocumentation() on next ABI change.
*/
- virtual Documentation retrieveModuleDocumentation(const QString& name) = 0;
+ virtual ModuleDocumentation retrieveModuleDocumentation(const QString &name) = 0;
static bool skipForQuery(const AbstractMetaFunctionCPtr &func);
static QString applyDocModifications(const DocModificationList &xpathMods, const QString &xml);
private:
- QString m_packageName;
QString m_docDataDir;
- QString m_libSourceDir;
static QString execXQuery(const XQueryPtr &xquery, const QString &query) ;
};
Documentation::Documentation(const QString &detailed,
const QString &brief,
- Format fmt) :
- m_detailed(detailed.trimmed()), m_brief(brief.trimmed()), m_format(fmt)
+ const QString &sourceFile,
+ DocumentationFormat fmt) :
+ m_detailed(detailed.trimmed()),
+ m_brief(brief.trimmed()),
+ m_sourceFile(sourceFile),
+ m_format(fmt)
{
}
return m_detailed.isEmpty() && m_brief.isEmpty();
}
-Documentation::Format Documentation::format() const
+DocumentationFormat Documentation::format() const
{
return m_format;
}
-void Documentation::setValue(const QString &value, Documentation::Type t)
+void Documentation::setValue(const QString &value, DocumentationType t)
{
- if (t == Brief)
+ if (t == DocumentationType::Brief)
setBrief(value);
else
setDetailed(value);
}
-void Documentation::setFormat(Documentation::Format f)
+void Documentation::setFormat(DocumentationFormat f)
{
m_format = f;
}
debug.nospace();
debug << "Documentation(";
if (!d.isEmpty()) {
- debug << "format=" << d.format();
+ debug << "format=" << (d.format() == DocumentationFormat::Native ? "native" : "target");
+ if (d.hasSourceFile())
+ debug << ", file=\"" << d.sourceFile() << '"';
if (!d.brief().isEmpty())
debug << ", brief=\"" << d.brief() << '"';
if (!d.detailed().isEmpty())
#ifndef DOCUMENTATION_H
#define DOCUMENTATION_H
+#include "documentation_enums.h"
+
#include <QtCore/QString>
#include <QtCore/QtCompare>
class Documentation
{
public:
- enum Format {
- Native, // XML
- Target // RST
- };
-
- enum Type {
- Detailed,
- Brief,
- Last
- };
-
Documentation() = default;
explicit Documentation(const QString &detailed,
const QString &brief,
- Format fmt = Documentation::Native);
+ const QString &sourceFile,
+ DocumentationFormat fmt = DocumentationFormat::Native);
bool isEmpty() const;
- void setValue(const QString& value, Type t = Documentation::Detailed);
+ void setValue(const QString& value, DocumentationType t = DocumentationType::Detailed);
- Documentation::Format format() const;
- void setFormat(Format f);
+ DocumentationFormat format() const;
+ void setFormat(DocumentationFormat f);
bool equals(const Documentation &rhs) const;
const QString &brief() const { return m_brief; }
void setBrief(const QString &brief);
+ bool hasSourceFile() const { return !m_sourceFile.isEmpty(); }
+ const QString &sourceFile() const { return m_sourceFile; }
+ void setSourceFile(const QString &newSourceFile) { m_sourceFile = newSourceFile; }
+
private:
friend bool comparesEqual(const Documentation &lhs,
const Documentation &rhs) noexcept
{
return lhs.m_format == rhs.m_format && lhs.m_detailed == rhs.m_detailed
- && lhs.m_brief == rhs.m_brief;
+ && lhs.m_brief == rhs.m_brief && lhs.m_sourceFile == rhs.m_sourceFile;
}
Q_DECLARE_EQUALITY_COMPARABLE(Documentation)
QString m_detailed;
QString m_brief;
- Format m_format = Documentation::Native;
+ QString m_sourceFile;
+ DocumentationFormat m_format = DocumentationFormat::Native;
};
#ifndef QT_NO_DEBUG_STREAM
--- /dev/null
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef DOCUMENTATION_ENUMS_H
+#define DOCUMENTATION_ENUMS_H
+
+#include <QtCore/QtTypes>
+
+enum class DocumentationFormat : uint8_t
+{
+ Native, // XML
+ Target // RST
+};
+
+enum class DocumentationEmphasis : uint8_t
+{
+ None,
+ LanguageNote
+};
+
+enum class DocumentationType : uint8_t
+{
+ Detailed,
+ Brief
+};
+
+#endif // DOCUMENTATION_ENUMS_H
return kind;
}
-Documentation DoxygenParser::retrieveModuleDocumentation()
-{
- return retrieveModuleDocumentation(packageName());
-}
-
-void DoxygenParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
+QString DoxygenParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
{
if (!metaClass)
- return;
+ return {};
QString doxyFileSuffix;
if (metaClass->enclosingClass()) {
<< "Can't find doxygen file for class " << metaClass->name() << ", tried: "
<< QDir::toNativeSeparators(documentationDataDirectory())
<< "/{struct|class|namespace}"<< doxyFileSuffix;
- return;
+ return {};
}
QString errorMessage;
XQueryPtr xquery = XQuery::create(doxyFilePath, &errorMessage);
if (!xquery) {
qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
- return;
+ return {};
}
- static const QList<std::pair<Documentation::Type, QString>> docTags = {
- { Documentation::Brief, u"briefdescription"_s },
- { Documentation::Detailed, u"detaileddescription"_s }
+ static const QList<std::pair<DocumentationType, QString>> docTags = {
+ { DocumentationType::Brief, u"briefdescription"_s },
+ { DocumentationType::Detailed, u"detaileddescription"_s }
};
// Get class documentation
Documentation classDoc;
+ classDoc.setSourceFile(doxyFilePath);
for (const auto &tag : docTags) {
const QString classQuery = u"/doxygen/compounddef/"_s + tag.second;
}
}
Documentation funcDoc;
+ funcDoc.setSourceFile(doxyFilePath);
for (const auto &tag : docTags) {
QString funcQuery(query);
if (!isProperty) {
//Fields
for (AbstractMetaField &field : metaClass->fields()) {
if (field.isPrivate())
- return;
+ continue;
Documentation fieldDoc;
+ fieldDoc.setSourceFile(doxyFilePath);
for (const auto &tag : docTags) {
QString query = u"/doxygen/compounddef/sectiondef/memberdef/name[text()=\""_s
+ field.name() + u"\"]/../"_s + tag.second;
qCWarning(lcShibokenDoc, "%s",
qPrintable(msgCannotFindDocumentation(doxyFilePath, metaClass, meta_enum, query)));
}
- meta_enum.setDocumentation(Documentation(doc, {}));
+ meta_enum.setDocumentation(Documentation(doc, {}, doxyFilePath));
}
+ return doxyFilePath;
}
-Documentation DoxygenParser::retrieveModuleDocumentation(const QString& name){
-
+ModuleDocumentation DoxygenParser::retrieveModuleDocumentation(const QString &name)
+{
QString sourceFile = documentationDataDirectory() + u"/indexpage.xml"_s;
if (!QFile::exists(sourceFile)) {
// Module documentation
QString query = u"/doxygen/compounddef/detaileddescription"_s;
const QString doc = getDocumentation(xquery, query, DocModificationList());
- return Documentation(doc, {});
+ return {Documentation(doc, {}, sourceFile), {}};
}
-
{
public:
DoxygenParser() = default;
- void fillDocumentation(const AbstractMetaClassPtr &metaClass) override;
- Documentation retrieveModuleDocumentation() override;
- Documentation retrieveModuleDocumentation(const QString& name) override;
+ QString fillDocumentation(const AbstractMetaClassPtr &metaClass) override;
+ ModuleDocumentation retrieveModuleDocumentation(const QString &name) override;
};
#endif // DOXYGENPARSER_H
class EnclosingClassMixin {
public:
- const AbstractMetaClassCPtr enclosingClass() const
+ AbstractMetaClassCPtr enclosingClass() const
{ return m_enclosingClass.lock(); }
void setEnclosingClass(const AbstractMetaClassCPtr &cls)
{ m_enclosingClass = cls; }
template <class Node>
class Graph
{
+ using IndexList = QList<qsizetype>;
+
public:
using NodeList = QList<Node>;
Graph() = default;
- // Construct from a sequence of nodes
+ // Construct from a QList of nodes (unchecked, does not require operator==())
+ explicit Graph(const NodeList &list) : m_nodes(list)
+ {
+ m_nodeEntries.resize(list.size());
+ }
+
+ // Construct from a sequence of nodes (checks for duplicated nodes using operator==())
template<class It>
- explicit Graph(It i1, It i2) { setNodes(i1, i2); }
+ explicit Graph(It i1, It i2)
+ {
+ const auto size = std::distance(i1, i2);
+ m_nodes.reserve(size);
+ m_nodeEntries.reserve(size);
+ setNodes(i1, i2);
+ }
template<class It>
void setNodes(It i1, It i2)
bool addNode(Node n);
/// Returns whether node was registered
- bool hasNode(Node node) { return indexOfNode(node) != -1; }
+ bool hasNode(Node node) { return m_nodes.contains(node); }
/// Returns the numbed of nodes in this graph.
qsizetype nodeCount() const { return m_nodeEntries.size(); }
/// Returns true if the graph contains the edge from -> to
bool containsEdge(Node from, Node to) const;
+ bool containsEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex) const;
/// Returns true if the graph has any edges
bool hasEdges() const;
/// Adds an edge to this graph.
bool addEdge(Node from, Node to);
+ bool addEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex);
/// Removes an edge from this graph.
bool removeEdge(Node from, Node to);
+ bool removeEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex);
/// Clears the graph
- void clear() { m_nodeEntries.clear(); }
+ void clear()
+ {
+ m_nodes.clear();
+ m_nodeEntries.clear();
+ }
/// Dumps a dot graph to a file named \p filename.
/// \param fileName file name where the output should be written.
GraphSortResult<Node> topologicalSort() const;
private:
- enum Color { WHITE, GRAY, BLACK };
+ enum Color : quint8 { WHITE, GRAY, BLACK };
struct NodeEntry
{
- Node node;
- NodeList targets;
- mutable Color color;
+ IndexList targets;
+ mutable Color color = WHITE;
};
Color colorAt(qsizetype i) const { return m_nodeEntries.at(i).color; }
- qsizetype indexOfNode(Node n) const;
- void depthFirstVisit(qsizetype i, NodeList &result) const;
+ void depthFirstVisit(qsizetype i, IndexList *result) const;
+ NodeList m_nodes;
QList<NodeEntry> m_nodeEntries;
};
-template <class Node>
-qsizetype Graph<Node>::indexOfNode(Node n) const
-{
- for (qsizetype i = 0, size = m_nodeEntries.size(); i < size; ++i) {
- if (m_nodeEntries.at(i).node == n)
- return i;
- }
- return -1;
-}
-
template <class Node>
bool Graph<Node>::addNode(Node n)
{
if (hasNode(n))
return false;
- m_nodeEntries.append({n, {}, WHITE});
+ m_nodes.append(n);
+ m_nodeEntries.append(NodeEntry{});
return true;
}
template <class Node>
-void Graph<Node>::depthFirstVisit(qsizetype i, NodeList &result) const
+void Graph<Node>::depthFirstVisit(qsizetype i, IndexList *result) const
{
m_nodeEntries[i].color = GRAY;
- for (auto to : m_nodeEntries[i].targets) {
- const qsizetype toIndex = indexOfNode(to);
- Q_ASSERT(toIndex != -1);
+ for (qsizetype toIndex : m_nodeEntries.at(i).targets) {
switch (colorAt(toIndex)) {
case WHITE:
depthFirstVisit(toIndex, result);
m_nodeEntries[i].color = BLACK;
- result.append(m_nodeEntries.at(i).node);
+ result->append(i);
}
template <class Node>
const qsizetype size = m_nodeEntries.size();
GraphSortResult<Node> result;
- result.result.reserve(size);
- if (hasEdges()) {
+ if (size > 1 && hasEdges()) {
+ result.result.reserve(size);
+ IndexList indexList;
+ indexList.reserve(size);
for (qsizetype i = 0; i < size; ++i)
m_nodeEntries[i].color = WHITE;
for (qsizetype i = 0; i < size; ++i) {
if (colorAt(i) == WHITE) // recursive calls may have set it to black
- depthFirstVisit(i, result.result);
+ depthFirstVisit(i, &indexList);
}
- } else { // no edges, shortcut
- std::transform(m_nodeEntries.cbegin(), m_nodeEntries.cend(),
- std::back_inserter(result.result),
- [](const NodeEntry &ne) { return ne.node; });
- }
-
- if (result.result.size() == size) {
- std::reverse(result.result.begin(), result.result.end());
- } else {
- for (const auto &nodeEntry : m_nodeEntries) {
- if (!result.result.contains(nodeEntry.node))
- result.cyclic.append(nodeEntry.node);
+ if (indexList.size() == size) { // Succeeded, all traversed
+ for (qsizetype i = size - 1; i >= 0; --i)
+ result.result.append(m_nodes.at(indexList.at(i)));
+ } else { // Cyclic, Not a DAG!
+ for (qsizetype i = 0; i < size; ++i) {
+ if (!indexList.contains(i))
+ result.cyclic.append(m_nodes.at(i));
+ }
}
- result.result.clear(); // Not a DAG!
+ } else { // no edges, shortcut. Legacy behavior: Also reverse in this case.
+ result.result = m_nodes;
+ std::reverse(result.result.begin(), result.result.end());
}
return result;
}
template <class Node>
bool Graph<Node>::containsEdge(Node from, Node to) const
{
- const qsizetype i = indexOfNode(from);
- return i != -1 && m_nodeEntries.at(i).targets.contains(to);
+ return containsEdgeByIndexes(m_nodes.indexOf(from), m_nodes.indexOf(to));
+}
+
+template <class Node>
+bool Graph<Node>::containsEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex) const
+{
+ return fromIndex >= 0 && fromIndex < m_nodeEntries.size()
+ && m_nodeEntries.at(fromIndex).targets.contains(toIndex);
}
template <class Node>
bool Graph<Node>::hasEdges() const
{
- for (const auto &nodeEntry : m_nodeEntries) {
- if (!nodeEntry.targets.isEmpty())
- return true;
- }
- return false;
+ auto hashEdgesPred = [](const NodeEntry &nodeEntry) { return !nodeEntry.targets.isEmpty(); };
+ return std::any_of(m_nodeEntries.cbegin(), m_nodeEntries.cend(), hashEdgesPred);
}
template <class Node>
bool Graph<Node>::addEdge(Node from, Node to)
{
- const qsizetype i = indexOfNode(from);
- if (i == -1 || !hasNode(to) || m_nodeEntries.at(i).targets.contains(to))
+ return addEdgeByIndexes(m_nodes.indexOf(from), m_nodes.indexOf(to));
+}
+
+template <class Node>
+bool Graph<Node>::addEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex)
+{
+ if (fromIndex < 0 || fromIndex >= m_nodeEntries.size()
+ || toIndex < 0 || toIndex >= m_nodeEntries.size()
+ || m_nodeEntries.at(fromIndex).targets.contains(toIndex)) {
return false;
- m_nodeEntries[i].targets.append(to);
+ }
+ m_nodeEntries[fromIndex].targets.append(toIndex);
return true;
}
template <class Node>
bool Graph<Node>::removeEdge(Node from, Node to)
{
- const qsizetype i = indexOfNode(from);
- if (i == -1)
+ return removeEdgeByIndexes(m_nodes.indexOf(from), m_nodes.indexOf(to));
+}
+
+template <class Node>
+bool Graph<Node>::removeEdgeByIndexes(qsizetype fromIndex, qsizetype toIndex)
+{
+ if (fromIndex < 0 || fromIndex >= m_nodeEntries.size()
+ || toIndex < 0 || toIndex >= m_nodeEntries.size()) {
return false;
- auto &targets = m_nodeEntries[i].targets;
- const qsizetype toIndex = targets.indexOf(to);
- if (toIndex == -1)
+ }
+ auto &targets = m_nodeEntries[fromIndex].targets;
+ const qsizetype toPos = targets.indexOf(toIndex);
+ if (toPos == -1)
return false;
- targets.removeAt(toIndex);
+ targets.removeAt(toPos);
return true;
}
NameFunction nameFunction) const
{
s << "digraph D {\n";
- for (const auto &nodeEntry : m_nodeEntries) {
+ for (qsizetype i = 0, size = m_nodes.size(); i < size; ++i) {
+ const auto &nodeEntry = m_nodeEntries.at(i);
if (!nodeEntry.targets.isEmpty()) {
- const QString fromName = nameFunction(nodeEntry.node);
- for (const Node &to : nodeEntry.targets)
- s << '"' << fromName << "\" -> \"" << nameFunction(to) << "\"\n";
+ const QString fromName = nameFunction(m_nodes.at(i));
+ for (qsizetype i : nodeEntry.targets)
+ s << '"' << fromName << "\" -> \"" << nameFunction(m_nodes.at(i)) << "\"\n";
}
}
s << "}\n";
const auto &nodeEntry = m_nodeEntries.at(i);
if (i)
debug << ", ";
- debug << nodeEntry.node;
+ debug << m_nodes.at(i);
if (!nodeEntry.targets.isEmpty()) {
debug << " -> [";
for (qsizetype t = 0, tsize = nodeEntry.targets.size(); t < tsize; ++t) {
if (t)
debug << ", ";
- debug << nodeEntry.targets.at(t);
+ debug << m_nodes.at(nodeEntry.targets.at(t));
}
debug << ']';
}
#include "abstractmetafield.h"
#include "abstractmetafunction.h"
#include "abstractmetalang.h"
+#include "include.h"
#include "modifications.h"
#include "sourcelocation.h"
#include "typedatabase.h"
#include <QtCore/QStringList>
#include <QtCore/QXmlStreamReader>
+#include <algorithm>
+#include <iterator>
+
using namespace Qt::StringLiterals;
// abstractmetabuilder.cpp
return result;
}
+QString msgStrippingQtDisambiguatedArgument(const FunctionModelItem &f,
+ const QString &originalSignature)
+{
+ QString result;
+ QTextStream str(&result);
+ str << f->sourceLocation() << "Stripping last argument of "
+ << originalSignature << " (QT6_DECL_NEW_OVERLOAD_TAIL).";
+ return result;
+}
+
QString msgEnumNotDefined(const EnumTypeEntryCPtr &t)
{
QString result;
{
return u"Invalid argument for language level: \""_s + l + u"\"."_s;
}
+
+QString msgCannotFindImage(const QString &href, const QString &context,
+ const QStringList &candidates)
+{
+ QString result = "Cannot resolve image "_L1 + href + " for "_L1 + context
+ + " (tried "_L1;
+ for (const auto &candidate : candidates)
+ result += QDir::toNativeSeparators(candidate) + u' ';
+ result += ")."_L1;
+ return result;
+}
+
+QString msgNotRelative(const QString &path, const QString &dir)
+{
+ return QDir::toNativeSeparators(path)
+ + " is not under "_L1 + QDir::toNativeSeparators(dir) + u'.';
+}
+
+QString msgCannotCreateDir(const QString &dir)
+{
+ return "Cannot create "_L1 + QDir::toNativeSeparators(dir) + u'.';
+}
+
+QString msgCannotCopy(const QFile &source, const QString &target)
+{
+ return "Cannot copy "_L1 + QDir::toNativeSeparators(source.fileName())
+ + " to "_L1 + QDir::toNativeSeparators(target)
+ + ": "_L1 + source.errorString();
+}
+
+QString msgCannotFindQDocFile(const AbstractMetaClassCPtr &metaClass,
+ const QStringList &candidates)
+{
+ QStringList nativeCandidates;
+ std::transform(candidates.cbegin(), candidates.cend(), std::back_inserter(nativeCandidates),
+ QDir::toNativeSeparators);
+ QString result;
+ QTextStream(&result) << "Cannot find qdoc file for "
+ << (metaClass->isNamespace() ? "namespace" : "class") << " \""
+ << metaClass->typeEntry()->qualifiedCppName() << "\" ("
+ << QDir::toNativeSeparators(metaClass->typeEntry()->include().name())
+ << "), tried: " << nativeCandidates.join(", "_L1);
+ return result;
+}
const ArgumentModelItem &arg,
const QString &reason);
+QString msgStrippingQtDisambiguatedArgument(const FunctionModelItem &f,
+ const QString &originalSignature);
+
QString msgEnumNotDefined(const EnumTypeEntryCPtr &t);
QString msgUnknownBase(const AbstractMetaClassCPtr &metaClass,
QString msgInvalidLanguageLevel(const QString &l);
+QString msgCannotFindImage(const QString &href, const QString &context,
+ const QStringList &candidates);
+
+QString msgNotRelative(const QString &path, const QString &dir);
+
+QString msgCannotCreateDir(const QString &dir);
+
+QString msgCannotCopy(const QFile &source, const QString &target);
+
+QString msgCannotFindQDocFile(const AbstractMetaClassCPtr &metaClass,
+ const QStringList &candidates);
+
#endif // MESSAGES_H
#define MODIFICATIONS_H
#include "typesystem_enums.h"
+#include "documentation_enums.h"
#include "modifications_typedefs.h"
#include <QtCore/QList>
return m_mode;
}
- TypeSystem::Language format() const { return m_format; }
- void setFormat(TypeSystem::Language f) { m_format = f; }
+ DocumentationFormat format() const { return m_format; }
+ void setFormat(DocumentationFormat f) { m_format = f; }
+
+ DocumentationEmphasis emphasis() const { return m_emphasis; }
+ void setEmphasis(DocumentationEmphasis newEmphasis) { m_emphasis = newEmphasis; }
private:
QString m_code;
QString m_xpath;
QString m_signature;
TypeSystem::DocModificationMode m_mode = TypeSystem::DocModificationXPathReplace;
- TypeSystem::Language m_format = TypeSystem::NativeCode;
+ DocumentationFormat m_format = DocumentationFormat::Native;
+ DocumentationEmphasis m_emphasis = DocumentationEmphasis::None;
};
#endif // MODIFICATIONS_H
return model()->findFile(fileName());
}
-void _CodeModelItem::getStartPosition(int *line, int *column)
+void _CodeModelItem::getStartPosition(int *line, int *column) const
{
*line = m_startLine;
*column = m_startColumn;
m_startColumn = column;
}
-void _CodeModelItem::getEndPosition(int *line, int *column)
+void _CodeModelItem::getEndPosition(int *line, int *column) const
{
*line = m_endLine;
*column = m_endColumn;
bool _ClassModelItem::extendsClass(const QString &name) const
{
- for (const BaseClass &bc : m_baseClasses) {
- if (bc.name == name)
- return true;
- }
- return false;
+ return std::any_of(m_baseClasses.cbegin(), m_baseClasses.cend(),
+ [&name](const BaseClass &bc) {
+ return bc.name == name;
+ });
}
_ClassModelItem::_ClassModelItem(CodeModel *model, int kind)
if (const auto e = scope->findEnumByValueHelper(fullValue, enumValue))
return e;
- if (auto *enclosingScope = scope->enclosingScope()) {
+ if (const auto *enclosingScope = scope->enclosingScope()) {
// The enclosing scope may have several sibling namespaces of that name.
if (searchSiblingNamespaces && scope->kind() == Kind_Namespace) {
- if (auto *enclosingNamespace = dynamic_cast<const _NamespaceModelItem *>(enclosingScope)) {
+ if (const auto *enclosingNamespace = dynamic_cast<const _NamespaceModelItem *>(enclosingScope)) {
for (const auto &sibling : enclosingNamespace->namespaces()) {
if (sibling.get() != scope && sibling->name() == scope->name()) {
if (const auto e = findEnumByValueRecursion(sibling.get(),
}
// PYSIDE-331: We need to also search the base classes.
- if (auto *classItem = dynamic_cast<const _ClassModelItem *>(scope)) {
+ if (const auto *classItem = dynamic_cast<const _ClassModelItem *>(scope)) {
for (const auto &base : classItem->baseClasses()) {
if (base.klass) {
auto *c = base.klass.get();
_NamespaceModelItem::~_NamespaceModelItem() = default;
-void _NamespaceModelItem::addNamespace(NamespaceModelItem item)
+void _NamespaceModelItem::addNamespace(const NamespaceModelItem &item)
{
item->setEnclosingScope(this);
m_namespaces.append(item);
FileModelItem file() const;
- void getStartPosition(int *line, int *column);
+ void getStartPosition(int *line, int *column) const;
int startLine() const { return m_startLine; }
void setStartPosition(int line, int column);
- void getEndPosition(int *line, int *column);
+ void getEndPosition(int *line, int *column) const;
void setEndPosition(int line, int column);
SourceLocation sourceLocation() const;
Q_DISABLE_COPY_MOVE(_ScopeModelItem)
DECLARE_MODEL_NODE(Scope)
- ~_ScopeModelItem();
+ ~_ScopeModelItem() override;
ClassList classes() const { return m_classes; }
const EnumList &enums() const { return m_enums; }
explicit _ClassModelItem(CodeModel *model, int kind = __node_kind);
explicit _ClassModelItem(CodeModel *model, const QString &name,
int kind = __node_kind);
- ~_ClassModelItem();
+ ~_ClassModelItem() override;
const QList<BaseClass> &baseClasses() const { return m_baseClasses; }
explicit _NamespaceModelItem(CodeModel *model, int kind = __node_kind);
explicit _NamespaceModelItem(CodeModel *model, const QString &name,
int kind = __node_kind);
- ~_NamespaceModelItem();
+ ~_NamespaceModelItem() override;
const NamespaceList &namespaces() const { return m_namespaces; }
NamespaceType type() const { return m_type; }
void setType(NamespaceType t) { m_type = t; }
- void addNamespace(NamespaceModelItem item);
+ void addNamespace(const NamespaceModelItem &item);
NamespaceModelItem findNamespace(QAnyStringView name) const;
using _NamespaceModelItem::_NamespaceModelItem;
- ~_FileModelItem();
+ ~_FileModelItem() override;
};
class _ArgumentModelItem: public _CodeModelItem
explicit _ArgumentModelItem(CodeModel *model, int kind = __node_kind);
explicit _ArgumentModelItem(CodeModel *model, const QString &name,
int kind = __node_kind);
- ~_ArgumentModelItem();
+ ~_ArgumentModelItem() override;
TypeInfo type() const;
void setType(const TypeInfo &type);
explicit _MemberModelItem(CodeModel *model, int kind = __node_kind);
explicit _MemberModelItem(CodeModel *model, const QString &name,
int kind = __node_kind);
- ~_MemberModelItem();
+ ~_MemberModelItem() override;
bool isConstant() const;
void setConstant(bool isConstant);
explicit _EnumModelItem(CodeModel *model, const QString &name, int kind = __node_kind);
explicit _EnumModelItem(CodeModel *model, int kind = __node_kind);
- ~_EnumModelItem();
+ ~_EnumModelItem() override;
Access accessPolicy() const;
void setAccessPolicy(Access accessPolicy);
explicit _EnumeratorModelItem(CodeModel *model, int kind = __node_kind);
explicit _EnumeratorModelItem(CodeModel *model, const QString &name,
int kind = __node_kind);
- ~_EnumeratorModelItem();
+ ~_EnumeratorModelItem() override;
QString stringValue() const;
void setStringValue(const QString &stringValue);
explicit _TemplateParameterModelItem(CodeModel *model, int kind = __node_kind);
explicit _TemplateParameterModelItem(CodeModel *model, const QString &name,
int kind = __node_kind);
- ~_TemplateParameterModelItem();
+ ~_TemplateParameterModelItem() override;
TypeInfo type() const;
void setType(const TypeInfo &type);
Type type() { return m_type; }
qint64 value() const { return m_value; }
quint64 unsignedValue() const { return m_unsignedValue; }
- bool isNullValue() const { return m_type == Signed ? m_value == 0 : m_unsignedValue == 0u; }
+ bool isNullValue() const { return m_type == Signed ? m_value == 0 : m_unsignedValue == 0U; }
bool isNegative() const { return m_type == Signed && m_value < 0; }
void setValue(qint64 v);
if (__rhs.referenceType() > __result.referenceType())
__result.setReferenceType(__rhs.referenceType());
- const auto indirections = __rhs.indirectionsV();
+ const auto &indirections = __rhs.indirectionsV();
for (auto i : indirections)
__result.addIndirection(i);
TypeInfo TypeInfo::resolveType(TypeInfo const &__type, const ScopeModelItem &__scope)
{
- CodeModel *__model = __scope->model();
- Q_ASSERT(__model != nullptr);
-
- return TypeInfo::resolveType(__model->findItem(__type.qualifiedName(), __scope), __type, __scope);
+ return TypeInfo::resolveType(CodeModel::findItem(__type.qualifiedName(), __scope),
+ __type, __scope);
}
-TypeInfo TypeInfo::resolveType(CodeModelItem __item, TypeInfo const &__type, const ScopeModelItem &__scope)
+TypeInfo TypeInfo::resolveType(const CodeModelItem &__item, TypeInfo const &__type,
+ const ScopeModelItem &__scope)
{
// Copy the type and replace with the proper qualified name. This
// only makes sence to do if we're actually getting a resolved
}
if (TypeDefModelItem __typedef = std::dynamic_pointer_cast<_TypeDefModelItem>(__item)) {
- const TypeInfo combined = TypeInfo::combine(__typedef->type(), otherType);
- const CodeModelItem nextItem = __scope->model()->findItem(combined.qualifiedName(), __scope);
+ TypeInfo combined = TypeInfo::combine(__typedef->type(), otherType);
+ const CodeModelItem nextItem = CodeModel::findItem(combined.qualifiedName(), __scope);
if (!nextItem)
return combined;
// PYSIDE-362, prevent recursion on opaque structs like
if (nextItem.get() ==__item.get()) {
std::cerr << "** WARNING Bailing out recursion of " << __FUNCTION__
<< "() on " << qPrintable(__type.qualifiedName().join(u"::"_s))
- << std::endl;
+ << '\n';
return otherType;
}
return resolveType(nextItem, combined, __scope);
friend class TypeInfoTemplateArgumentHandler;
- static TypeInfo resolveType(CodeModelItem item, TypeInfo const &__type, const ScopeModelItem &__scope);
+ static TypeInfo resolveType(const CodeModelItem &item, TypeInfo const &__type,
+ const ScopeModelItem &__scope);
};
#ifndef QT_NO_DEBUG_STREAM
static QString pyDictToCppMap(bool isQMap)
{
- return uR"(PyObject *key;
-PyObject *value;
+ return uR"(PyObject *key{};
+PyObject *value{};
%out.clear();
Py_ssize_t pos = 0;
while (PyDict_Next(%in, &pos, &key, &value)) {
// Convert Dict of Lists to a STL or Qt multi hash/map
static QString pyDictToCppMultiHash(bool isQMultiHash)
{
- return uR"(PyObject *key;
- PyObject *values;
+ return uR"(PyObject *key{};
+ PyObject *values{};
%out.clear();
Py_ssize_t pos = 0;
while (PyDict_Next(%in, &pos, &key, &values)) {
// QPair/std::pair
{u"shiboken_conversion_pysequence_to_cpppair"_s,
- uR"(%out.first = %CONVERTTOCPP[%OUTTYPE_0](PySequence_Fast_GET_ITEM(%in, 0));
-%out.second = %CONVERTTOCPP[%OUTTYPE_1](PySequence_Fast_GET_ITEM(%in, 1));
+ uR"(Shiboken::AutoDecRef objfirst(PySequence_GetItem(%in, 0));
+Shiboken::AutoDecRef objsecond(PySequence_GetItem(%in, 1));
+%out.first = %CONVERTTOCPP[%OUTTYPE_0](objfirst);
+%out.second = %CONVERTTOCPP[%OUTTYPE_1](objsecond);
)"_s},
{u"shiboken_conversion_cpppair_to_pytuple"_s,
uR"(PyObject *%out = PyTuple_New(2);
-PyTuple_SET_ITEM(%out, 0, %CONVERTTOPYTHON[%INTYPE_0](%in.first));
-PyTuple_SET_ITEM(%out, 1, %CONVERTTOPYTHON[%INTYPE_1](%in.second));
+PyTuple_SetItem(%out, 0, %CONVERTTOPYTHON[%INTYPE_0](%in.first));
+PyTuple_SetItem(%out, 1, %CONVERTTOPYTHON[%INTYPE_1](%in.second));
return %out;
)"_s},
TypeSystemProperty QPropertySpec::typeSystemPropertyFromQ_Property(const QString &declarationIn,
QString *errorMessage)
{
- enum class PropertyToken { None, Read, Write, Designable, Reset, Notify };
+ enum class PropertyToken { None, Read, Write, Designable, Reset, Notify, Member };
static const QHash<QString, PropertyToken> tokenLookup = {
{"READ"_L1, PropertyToken::Read},
{"WRITE"_L1, PropertyToken::Write},
{"DESIGNABLE"_L1, PropertyToken::Designable},
{"RESET"_L1, PropertyToken::Reset},
- {"NOTIFY"_L1, PropertyToken::Notify}
+ {"NOTIFY"_L1, PropertyToken::Notify},
+ {"MEMBER"_L1, PropertyToken::Member}
};
errorMessage->clear();
case PropertyToken::Notify:
result.notify = propertyTokens.at(pos + 1);
break;
+ case PropertyToken::Member:
+ // Ignore MEMBER tokens introduced by QTBUG-16852 as Python
+ // properties are anyways generated for fields.
+ return {};
case PropertyToken::None:
break;
#include "abstractmetalang.h"
#include "abstractmetatype.h"
#include "documentation.h"
+#include "exception.h"
#include "modifications.h"
#include "messages.h"
#include "propertyspec.h"
#include "complextypeentry.h"
#include "functiontypeentry.h"
#include "enumtypeentry.h"
+#include "typesystemtypeentry.h"
+#include "typedatabase.h"
#include "qtcompat.h"
#include <QtCore/QDir>
#include <QtCore/QFile>
+#include <QtCore/QHash>
#include <QtCore/QUrl>
+#include <algorithm>
+#include <iterator>
+
using namespace Qt::StringLiterals;
enum { debugFunctionSearch = 0 };
constexpr auto briefEndElement = "</brief>"_L1;
constexpr auto webxmlSuffix = ".webxml"_L1;
-Documentation QtDocParser::retrieveModuleDocumentation()
+// Return the package of a type "PySide6.QtGui.QPainter" -> "PySide6.QtGui"
+static QStringView packageFromPythonType(QStringView pythonType)
+{
+ qsizetype pos = pythonType.startsWith(u"PySide6.") ? 8 : 0;
+ auto dot = pythonType.indexOf(u'.', pos);
+ return dot != -1 ? pythonType.sliced(0, dot) : pythonType;
+}
+
+// Return the qdoc dir "PySide6.QtGui.QPainter" -> "qtgui/webxml" (QTBUG-119500)
+static QString qdocModuleDirFromPackage(QStringView package)
+{
+ if (package.startsWith(u"PySide6."))
+ package = package.sliced(8);
+ return package.toString().toLower() + "/webxml"_L1;
+}
+
+// Populate a cache of package to WebXML dir
+static QHash<QString, QString> getPackageToModuleDir()
{
- return retrieveModuleDocumentation(packageName());
+ QHash<QString, QString> result;
+ const auto &typeSystemEntries = TypeDatabase::instance()->typeSystemEntries();
+ for (const auto &te : typeSystemEntries) {
+ const QString &package = te->name();
+ const QString &docPackage = te->hasDocTargetLangPackage()
+ ? te->docTargetLangPackage() : package;
+ result.insert(package, qdocModuleDirFromPackage(docPackage));
+ }
+ return result;
+}
+
+QString QtDocParser::qdocModuleDir(const QString &pythonType)
+{
+ static const QHash<QString, QString> packageToModuleDir = getPackageToModuleDir();
+
+ const QStringView package = packageFromPythonType(pythonType);
+ const auto it = packageToModuleDir.constFind(package);
+ if (it == packageToModuleDir.cend()) {
+ const QString known = packageToModuleDir.keys().join(", "_L1);
+ qCWarning(lcShibokenDoc, "Type from unknown package: \"%s\" (known: %s).",
+ qPrintable(pythonType), qPrintable(known));
+ return qdocModuleDirFromPackage(package);
+ }
+ return it.value();
+}
+
+static QString xmlFileBaseName(const AbstractMetaClassPtr &metaClass)
+{
+ QString className = metaClass->qualifiedCppName().toLower();
+ className.replace("::"_L1, "-"_L1);
+ return className;
}
static void formatPreQualifications(QTextStream &str, const AbstractMetaType &type)
const auto flagsEntry = std::static_pointer_cast<const FlagsTypeEntry>(metaType.typeEntry());
QString name = flagsEntry->qualifiedCppName();
if (name.endsWith(u'>') && name.startsWith(u"QFlags<")) {
- const int lastColon = name.lastIndexOf(u':');
+ const auto lastColon = name.lastIndexOf(u':');
if (lastColon != -1) {
name.replace(lastColon + 1, name.size() - lastColon - 1, metaType.name());
name.remove(0, 7);
break;
case AbstractMetaType::ContainerPattern: { // QVector<int>
str << metaType.typeEntry()->qualifiedCppName() << '<';
- const auto instantiations = metaType.instantiations();
+ const auto &instantiations = metaType.instantiations();
for (qsizetype i = 0, size = instantiations.size(); i < size; ++i) {
if (i)
str << ", ";
return result;
}
-QString QtDocParser::functionDocumentation(const QString &sourceFileName,
- const ClassDocumentation &classDocumentation,
- const AbstractMetaClassCPtr &metaClass,
- const AbstractMetaFunctionCPtr &func,
- QString *errorMessage)
+QtDocParser::FunctionDocumentationOpt
+ QtDocParser::functionDocumentation(const QString &sourceFileName,
+ const ClassDocumentation &classDocumentation,
+ const AbstractMetaClassCPtr &metaClass,
+ const AbstractMetaFunctionCPtr &func, QString *errorMessage)
{
errorMessage->clear();
- const QString docString =
- queryFunctionDocumentation(sourceFileName, classDocumentation, metaClass,
- func, errorMessage);
+ FunctionDocumentationOpt orig = queryFunctionDocumentation(sourceFileName, classDocumentation, metaClass,
+ func, errorMessage);
+ if (!orig.has_value() || orig.value().description.isEmpty())
+ return orig;
const auto funcModifs = DocParser::getXpathDocModifications(func, metaClass);
- return docString.isEmpty() || funcModifs.isEmpty()
- ? docString : applyDocModifications(funcModifs, docString);
+ if (funcModifs.isEmpty())
+ return orig;
+
+ FunctionDocumentation modified = orig.value();
+ modified.description = applyDocModifications(funcModifs, orig->description);
+ return modified;
}
-QString QtDocParser::queryFunctionDocumentation(const QString &sourceFileName,
- const ClassDocumentation &classDocumentation,
- const AbstractMetaClassCPtr &metaClass,
- const AbstractMetaFunctionCPtr &func,
- QString *errorMessage)
+QtDocParser::FunctionDocumentationOpt
+ QtDocParser::queryFunctionDocumentation(const QString &sourceFileName,
+ const ClassDocumentation &classDocumentation,
+ const AbstractMetaClassCPtr &metaClass,
+ const AbstractMetaFunctionCPtr &func, QString *errorMessage)
{
// Search candidates by name and const-ness
FunctionDocumentationList candidates =
if (candidates.isEmpty()) {
*errorMessage = msgCannotFindDocumentation(sourceFileName, func.get())
+ u" (no matches)"_s;
- return {};
+ return std::nullopt;
}
// Try an exact query
}
if (index != -1)
- return candidates.at(index).description;
+ return candidates.at(index);
// Fallback: Try matching by argument count
const auto parameterCount = func->arguments().size();
QTextStream(errorMessage) << msgFallbackForDocumentation(sourceFileName, func.get())
<< "\n Falling back to \"" << match.signature
<< "\" obtained by matching the argument count only.";
- return candidates.constFirst().description;
+ return candidates.constFirst();
}
QTextStream(errorMessage) << msgCannotFindDocumentation(sourceFileName, func.get())
<< " (" << candidates.size() << " candidates matching the argument count)";
- return {};
+ return std::nullopt;
}
// Extract the <brief> section from a WebXML (class) documentation and remove it
return briefValue;
}
+// Apply the documentation parsed from WebXML to a AbstractMetaFunction and complete argument
+// names missing from parsed headers using the WebXML names (exact match only).
+static void applyDocumentation(const FunctionDocumentation &funcDoc,
+ const QString &sourceFileName,
+ const AbstractMetaFunctionPtr &func)
+{
+ const Documentation documentation(funcDoc.description, {}, sourceFileName);
+ func->setDocumentation(documentation);
+
+ if (const auto argCount = func->arguments().size(); argCount == funcDoc.parameterNames.size()) {
+ for (qsizetype a = 0; a < argCount; ++a) {
+ if (!func->arguments().at(a).hasName() && !funcDoc.parameterNames.at(a).isEmpty())
+ func->setArgumentName(a, funcDoc.parameterNames.at(a));
+ }
+ }
+}
+
// Find the webxml file for global functions/enums
// by the doc-file typesystem attribute or via include file.
static QString findGlobalWebXmLFile(const QString &documentationDataDirectory,
+ const QString &package,
const QString &docFile,
const Include &include)
{
QString result;
+ const QString root = documentationDataDirectory + u'/'
+ + QtDocParser::qdocModuleDir(package) + u'/';
if (!docFile.isEmpty()) {
- result = documentationDataDirectory + u'/' + docFile;
+ result = root + docFile;
if (!result.endsWith(webxmlSuffix))
result += webxmlSuffix;
return QFileInfo::exists(result) ? result : QString{};
if (include.name().isEmpty())
return {};
// qdoc "\headerfile <QtLogging>" directive produces "qtlogging.webxml"
- result = documentationDataDirectory + u'/' +
- QFileInfo(include.name()).baseName() + webxmlSuffix;
+ result = root + QFileInfo(include.name()).baseName() + webxmlSuffix;
if (QFileInfo::exists(result))
return result;
// qdoc "\headerfile <qdrawutil.h>" produces "qdrawutil-h.webxml"
return;
const QString sourceFileName =
- findGlobalWebXmLFile(documentationDataDirectory(), te->docFile(), te->include());
+ findGlobalWebXmLFile(documentationDataDirectory(), te->targetLangPackage(), te->docFile(), te->include());
if (sourceFileName.isEmpty())
return;
QString errorMessage;
- auto classDocumentationO = parseWebXml(sourceFileName, &errorMessage);
+ auto classDocumentationO = parseWebXml({sourceFileName}, &errorMessage);
if (!classDocumentationO.has_value()) {
qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
return;
}
- const QString detailed =
- functionDocumentation(sourceFileName, classDocumentationO.value(),
- {}, f, &errorMessage);
- if (!errorMessage.isEmpty())
+
+ const auto funcDocOpt = functionDocumentation(sourceFileName, classDocumentationO.value(),
+ {}, f, &errorMessage);
+ if (funcDocOpt.has_value())
+ applyDocumentation(funcDocOpt.value(), sourceFileName, f);
+ else if (!errorMessage.isEmpty())
qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
- const Documentation documentation(detailed, {});
- f->setDocumentation(documentation);
}
void QtDocParser::fillGlobalEnumDocumentation(AbstractMetaEnum &e)
{
auto te = e.typeEntry();
const QString sourceFileName =
- findGlobalWebXmLFile(documentationDataDirectory(), te->docFile(), te->include());
+ findGlobalWebXmLFile(documentationDataDirectory(), te->targetLangPackage(), te->docFile(), te->include());
if (sourceFileName.isEmpty())
return;
QString errorMessage;
- auto classDocumentationO = parseWebXml(sourceFileName, &errorMessage);
+ auto classDocumentationO = parseWebXml({sourceFileName}, &errorMessage);
if (!classDocumentationO.has_value()) {
qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
return;
}
- if (!extractEnumDocumentation(classDocumentationO.value(), e)) {
+ if (!extractEnumDocumentation(classDocumentationO.value(), sourceFileName, e)) {
qCWarning(lcShibokenDoc, "%s",
qPrintable(msgCannotFindDocumentation(sourceFileName, {}, e, {})));
}
}
-void QtDocParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
+QString QtDocParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
{
if (!metaClass)
- return;
+ return {};
auto context = metaClass->enclosingClass();
while (context) {
context = context->enclosingClass();
}
- QString sourceFileRoot = documentationDataDirectory() + u'/'
- + metaClass->qualifiedCppName().toLower();
- sourceFileRoot.replace(u"::"_s, u"-"_s);
-
- QFileInfo sourceFile(sourceFileRoot + webxmlSuffix);
- if (!sourceFile.exists())
- sourceFile.setFile(sourceFileRoot + ".xml"_L1);
- if (!sourceFile.exists()) {
- qCWarning(lcShibokenDoc).noquote().nospace()
- << "Can't find qdoc file for class " << metaClass->name() << ", tried: "
- << QDir::toNativeSeparators(sourceFile.absoluteFilePath());
- return;
+ // Find qdoc files of a class.
+ QStringList allCandidates;
+ const auto typeEntry = metaClass->typeEntry();
+ const QString docDir = documentationDataDirectory() + u'/'
+ + QtDocParser::qdocModuleDir(typeEntry->targetLangPackage()) + u'/';
+ const QString baseName = xmlFileBaseName(metaClass);
+ allCandidates.append(docDir + baseName + webxmlSuffix);
+ const QString &docFile = typeEntry->docFile();
+ if (!docFile.isEmpty())
+ allCandidates.append(docDir + docFile + webxmlSuffix);
+ allCandidates.append(docDir + baseName + ".xml"_L1);
+ QStringList candidates;
+ std::copy_if(allCandidates.cbegin(), allCandidates.cend(), std::back_inserter(candidates),
+ qOverload<const QString &>(QFileInfo::exists));
+
+ if (candidates.isEmpty()) {
+ qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotFindQDocFile(metaClass, allCandidates)));
+ return {};
}
- const QString sourceFileName = sourceFile.absoluteFilePath();
QString errorMessage;
-
- const auto classDocumentationO = parseWebXml(sourceFileName, &errorMessage);
+ const auto classDocumentationO = parseWebXml(candidates, &errorMessage);
if (!classDocumentationO.has_value()) {
qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
- return;
+ return {};
}
const auto &classDocumentation = classDocumentationO.value();
+ const QString &sourceFileName = candidates.constFirst();
for (const auto &p : classDocumentation.properties) {
- Documentation doc(p.description, p.brief);
+ Documentation doc(p.description, p.brief, sourceFileName);
metaClass->setPropertyDocumentation(p.name, doc);
}
const QString brief = extractBrief(&docString);
Documentation doc;
+ doc.setSourceFile(sourceFileName);
if (!brief.isEmpty())
- doc.setValue(brief, Documentation::Brief);
+ doc.setValue(brief, DocumentationType::Brief);
doc.setValue(docString);
metaClass->setDocumentation(doc);
//Functions Documentation
const auto &funcs = DocParser::documentableFunctions(metaClass);
for (const auto &func : funcs) {
- const QString detailed =
- functionDocumentation(sourceFileName, classDocumentation,
- metaClass, func, &errorMessage);
- if (!errorMessage.isEmpty())
+ const auto funcDocOpt = functionDocumentation(sourceFileName, classDocumentation,
+ metaClass, func, &errorMessage);
+ if (funcDocOpt.has_value()) {
+ applyDocumentation(funcDocOpt.value(), sourceFileName,
+ std::const_pointer_cast<AbstractMetaFunction>(func));
+ } else if (!errorMessage.isEmpty()) {
qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
- const Documentation documentation(detailed, {});
- std::const_pointer_cast<AbstractMetaFunction>(func)->setDocumentation(documentation);
+ }
}
#if 0
// Fields
#endif
// Enums
for (AbstractMetaEnum &meta_enum : metaClass->enums()) {
- if (!extractEnumDocumentation(classDocumentation, meta_enum)) {
+ if (!extractEnumDocumentation(classDocumentation, sourceFileName, meta_enum)) {
qCWarning(lcShibokenDoc, "%s",
qPrintable(msgCannotFindDocumentation(sourceFileName, metaClass, meta_enum, {})));
}
}
+
+ return sourceFileName;
}
bool QtDocParser::extractEnumDocumentation(const ClassDocumentation &classDocumentation,
+ const QString &sourceFileName,
AbstractMetaEnum &meta_enum)
{
- Documentation enumDoc;
const auto index = classDocumentation.indexOfEnum(meta_enum.name());
if (index == -1)
return false;
doc.insert(firstPara + 6, note);
}
}
- enumDoc.setValue(doc);
+ Documentation enumDoc(doc, {}, sourceFileName);
meta_enum.setDocumentation(enumDoc);
return true;
}
static QString qmlReferenceLink(const QFileInfo &qmlModuleFi)
{
- QString result;
- QTextStream(&result) << "<para>The module also provides <link"
- << R"( type="page" page="https://doc.qt.io/qt-)" << QT_VERSION_MAJOR
- << '/' << qmlModuleFi.baseName() << R"(.html")"
- << ">QML types</link>.</para>";
- return result;
+ return "https://doc.qt.io/qt-"_L1 + QString::number(QT_VERSION_MAJOR)
+ + u'/' + qmlModuleFi.baseName() + ".html"_L1;
}
-Documentation QtDocParser::retrieveModuleDocumentation(const QString& name)
+ModuleDocumentation QtDocParser::retrieveModuleDocumentation(const QString &name)
{
// TODO: This method of acquiring the module name supposes that the target language uses
// dots as module separators in package names. Improve this.
- QString moduleName = name;
- moduleName.remove(0, name.lastIndexOf(u'.') + 1);
- if (moduleName == u"QtQuickControls2")
- moduleName.chop(1);
- const QString prefix = documentationDataDirectory() + u'/'
- + moduleName.toLower();
-
- const QString sourceFile = prefix + u"-index.webxml"_s;
+ QString completeModuleName = name;
+ if (completeModuleName.endsWith("QtQuickControls2"_L1))
+ completeModuleName.chop(1);
+ const QString moduleName = completeModuleName.sliced(name.lastIndexOf(u'.') + 1);
+ const QString lowerModuleName = moduleName.toLower();
+
+ const QString dirPath = documentationDataDirectory() + u'/' + qdocModuleDir(completeModuleName);
+ const QString sourceFile = dirPath + u'/' + lowerModuleName + "-index.webxml"_L1;
if (!QFile::exists(sourceFile)) {
qCWarning(lcShibokenDoc).noquote().nospace()
<< "Can't find qdoc file for module " << name << ", tried: "
<< QDir::toNativeSeparators(sourceFile);
- return Documentation();
+ return {};
}
QString errorMessage;
qCWarning(lcShibokenDoc, "%s", qPrintable(errorMessage));
return {};
}
-
- Documentation doc(docString, {});
- if (doc.isEmpty()) {
+ if (docString.isEmpty()) {
qCWarning(lcShibokenDoc, "%s",
qPrintable(msgCannotFindDocumentation(sourceFile, "module", name)));
- return doc;
+ return {};
}
- // If a QML module info file exists, insert a link to the Qt docs.
- const QFileInfo qmlModuleFi(prefix + u"-qmlmodule.webxml"_s);
- if (qmlModuleFi.isFile()) {
- QString docString = doc.detailed();
- const int pos = docString.lastIndexOf(u"</description>");
- if (pos != -1) {
- docString.insert(pos, qmlReferenceLink(qmlModuleFi));
- doc.setDetailed(docString);
- }
- }
+ ModuleDocumentation result{Documentation{docString, {}, sourceFile}, {}};
- return doc;
+ // If a QML module info file exists, insert a link to the Qt docs.
+ // Use a filter as some file names are irregular.
+ // Note: These files are empty; we need to point to the web docs.
+ const QFileInfoList qmlModuleFiles =
+ QDir(dirPath).entryInfoList({"*-qmlmodule.webxml"_L1}, QDir::Files);
+ if (!qmlModuleFiles.isEmpty())
+ result.qmlTypesUrl = qmlReferenceLink(qmlModuleFiles.constFirst());
+ return result;
}
#define QTDOCPARSER_H
#include "docparser.h"
+#include <optional>
struct ClassDocumentation;
+struct FunctionDocumentation;
class QtDocParser : public DocParser
{
public:
QtDocParser() = default;
- void fillDocumentation(const AbstractMetaClassPtr &metaClass) override;
+ QString fillDocumentation(const AbstractMetaClassPtr &metaClass) override;
void fillGlobalFunctionDocumentation(const AbstractMetaFunctionPtr &f) override;
void fillGlobalEnumDocumentation(AbstractMetaEnum &e) override;
- Documentation retrieveModuleDocumentation() override;
- Documentation retrieveModuleDocumentation(const QString& name) override;
+ ModuleDocumentation retrieveModuleDocumentation(const QString &name) override;
+
+ static QString qdocModuleDir(const QString &pythonType);
private:
- static QString functionDocumentation(const QString &sourceFileName,
- const ClassDocumentation &classDocumentation,
- const AbstractMetaClassCPtr &metaClass,
- const AbstractMetaFunctionCPtr &func,
- QString *errorMessage);
-
- static QString queryFunctionDocumentation(const QString &sourceFileName,
- const ClassDocumentation &classDocumentation,
- const AbstractMetaClassCPtr &metaClass,
- const AbstractMetaFunctionCPtr &func,
- QString *errorMessage);
+ using FunctionDocumentationOpt = std::optional<FunctionDocumentation>;
+
+ static FunctionDocumentationOpt
+ functionDocumentation(const QString &sourceFileName,
+ const ClassDocumentation &classDocumentation,
+ const AbstractMetaClassCPtr &metaClass,
+ const AbstractMetaFunctionCPtr &func, QString *errorMessage);
+ static FunctionDocumentationOpt
+ queryFunctionDocumentation(const QString &sourceFileName,
+ const ClassDocumentation &classDocumentation,
+ const AbstractMetaClassCPtr &metaClass,
+ const AbstractMetaFunctionCPtr &func, QString *errorMessage);
static bool extractEnumDocumentation(const ClassDocumentation &classDocumentation,
+ const QString &sourceFileName,
AbstractMetaEnum &meta_enum);
};
#endif // QTDOCPARSER_H
-
void ReportHandler::messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &text)
{
// Check for file location separator added by SourceLocation
- int fileLocationPos = text.indexOf(u":\t");
+ auto fileLocationPos = text.indexOf(u":\t");
if (type == QtWarningMsg) {
if (m_silent || m_reportedWarnings.contains(text))
return;
- if (auto db = TypeDatabase::instance()) {
+ if (auto *db = TypeDatabase::instance()) {
const bool suppressed = fileLocationPos >= 0
? db->isSuppressedWarning(QStringView{text}.mid(fileLocationPos + 2))
: db->isSuppressedWarning(text);
if (NOT DISABLE_DOCSTRINGS)
declare_test(testmodifydocumentation)
endif()
-
}
QTEST_APPLESS_MAIN(TestContainer)
-
}
QTEST_APPLESS_MAIN(TestDtorInformation)
-
-
}
QTEST_APPLESS_MAIN(TestFunctionTag)
-
// cannot handle Qt resources.
QTemporaryDir tempDir(QDir::tempPath() + u"/shiboken_testmodifydocXXXXXX"_s);
QVERIFY2(tempDir.isValid(), qPrintable(tempDir.errorString()));
+ constexpr auto docDir = "foo/webxml/"_L1;
constexpr auto docFileName = "a.xml"_L1;
- QVERIFY(QFile::copy(u":/"_s + docFileName, tempDir.filePath(docFileName)));
+ {
+ QDir dir(tempDir.path());
+ QVERIFY(dir.mkpath(docDir));
+ }
+ QVERIFY(QFile::copy(u":/"_s + docFileName, tempDir.path() + u'/' + docDir + u'/' + docFileName));
QtDocParser docParser;
docParser.setDocumentationDataDirectory(tempDir.path());
}
QTEST_APPLESS_MAIN(NamespaceTest)
-
}
QTEST_APPLESS_MAIN(TestNumericalTypedef)
-
}
QTEST_APPLESS_MAIN(TestPrimitiveTypeTag)
-
QTEST_APPLESS_MAIN(TestRefCountTag)
-
-
}
QTEST_APPLESS_MAIN(TestReferenceToPointer)
-
-
}
QTEST_APPLESS_MAIN(TestRemoveField)
-
-
}
QTEST_APPLESS_MAIN(TestRemoveOperatorMethod)
-
}
QTEST_APPLESS_MAIN(TestReverseOperators)
-
AbstractMetaFunctionCList globalFuncs = builder->globalFunctions();
QCOMPARE(globalFuncs.size(), 1);
- const auto func = globalFuncs.constFirst();
+ const auto &func = globalFuncs.constFirst();
QCOMPARE(func->minimalSignature(), u"func(List<int>*)");
QCOMPARE(func->arguments().constFirst().type().cppSignature(),
u"List<int> *");
const auto globalFuncs = builder->globalFunctions();
QCOMPARE(globalFuncs.size(), 1);
- const auto func = globalFuncs.constFirst();
+ const auto &func = globalFuncs.constFirst();
QCOMPARE(func->minimalSignature(), u"func(List<int>&)");
QCOMPARE(func->arguments().constFirst().type().cppSignature(),
u"List<int> &");
const auto method = vector->findFunction("method");
QVERIFY(method);
- QCOMPARE(method->signature(), u"method(const Vector<int> & vector)");
+ QCOMPARE(method->signature(), "method(const Vector<int> &vector)"_L1);
const auto otherMethod = vector->findFunction("otherMethod");
QVERIFY(otherMethod);
}
QTEST_APPLESS_MAIN(TestTopoSort)
-
}
QTEST_APPLESS_MAIN(TestTypeRevision)
-
-
QChar TextStream::lastChar() const
{
- auto s = m_str.string();
+ const auto *s = m_str.string();
return s != nullptr && !s->isEmpty() ? *(s->crbegin()) : QChar();
}
qint64 TextStream::pos() const
{
// QTextStream::pos() only works for QIODevice, be a bit smarter
- if (auto s = m_str.string())
+ if (const auto *s = m_str.string())
return s->size();
// QIODevices need to flushed to tell the correct position.
const_cast<QTextStream &>(m_str).flush();
{u"PyObject"_s, u"true"_s, TypeSystem::CPythonType::Other},
// shiboken-specific
{u"PyPathLike"_s, u"Shiboken::String::checkPath"_s, TypeSystem::CPythonType::Other},
- {u"PySequence"_s, u"Shiboken::String::checkIterable"_s, TypeSystem::CPythonType::Other},
+ {u"PySequence"_s, u"Shiboken::String::checkIterableArgument"_s,
+ TypeSystem::CPythonType::Other},
{u"PyUnicode"_s, u"PyUnicode_Check"_s, TypeSystem::CPythonType::String},
{u"PyTypeObject"_s, u"PyType_Check"_s, TypeSystem::CPythonType::Other},
{u"str"_s, u"Shiboken::String::check"_s, TypeSystem::CPythonType::String},
return result;
}
+QList<TypeSystemTypeEntryCPtr> TypeDatabase::typeSystemEntries() const
+{
+ return d->m_typeSystemEntries;
+}
+
TypeSystemTypeEntryCPtr TypeDatabasePrivate::defaultTypeSystemType() const
{
return m_typeSystemEntries.value(0, nullptr);
{
const auto &instantiations = context->smartPointerInstantiations;
for (auto it = instantiations.cbegin(), end = instantiations.cend(); it != end; ++it) {
- auto smartPointerEntry = it.key();
+ const auto &smartPointerEntry = it.key();
const auto instantiationNames = splitTypeList(it.value());
SmartPointerTypeEntry::Instantiations instantiations;
instantiations.reserve(instantiationNames.size());
TypeSystemTypeEntryCPtr findTypeSystemType(const QString &name) const;
TypeSystemTypeEntryCPtr defaultTypeSystemType() const;
QString loadedTypeSystemNames() const;
+ QList<TypeSystemTypeEntryCPtr > typeSystemEntries() const;
QString defaultPackageName() const;
TypeEntryPtr findType(const QString &name) const;
InvalidToken
};
- Scanner(const QString &s)
- : m_pos(0), m_length(s.length()), m_tokenStart(-1), m_chars(s.constData())
+ Scanner(const QString &s) :
+ m_length(s.length()), m_chars(s.constData())
{
}
QString msgParseError(const QString &why) const;
private:
- int m_pos;
- int m_length;
- int m_tokenStart;
+ qsizetype m_pos = 0;
+ qsizetype m_length;
+ qsizetype m_tokenStart = -1;
const QChar *m_chars;
};
{
if (m_d->m_type != SmartPointerType)
return false;
- auto *ste = static_cast<const SmartPointerTypeEntry *>(this);
+ const auto *ste = static_cast<const SmartPointerTypeEntry *>(this);
return ste->smartPointerType() == TypeSystem::SmartPointerType::Unique;
}
{
if (m_d->m_cachedTargetLangEntryName.isEmpty()) {
m_d->m_cachedTargetLangEntryName = targetLangName();
- const int lastDot = m_d->m_cachedTargetLangEntryName.lastIndexOf(u'.');
+ const auto lastDot = m_d->m_cachedTargetLangEntryName.lastIndexOf(u'.');
if (lastDot != -1)
m_d->m_cachedTargetLangEntryName.remove(0, lastDot + 1);
}
QString m_subModuleOf;
QString m_namespaceBegin;
QString m_namespaceEnd;
+ QString m_docTargetLangPackage;
+ TypeSystem::DocMode m_docMode = TypeSystem::DocMode::Nested;
};
TypeSystemTypeEntry::TypeSystemTypeEntry(const QString &entryName, const QVersionNumber &vr,
d->m_subModuleOf = s;
}
+bool TypeSystemTypeEntry::hasDocTargetLangPackage() const
+{
+ S_D(const TypeSystemTypeEntry);
+ return !d->m_docTargetLangPackage.isEmpty();
+}
+
+QString TypeSystemTypeEntry::docTargetLangPackage() const
+{
+ S_D(const TypeSystemTypeEntry);
+ return d->m_docTargetLangPackage;
+}
+
+void TypeSystemTypeEntry::setDocTargetLangPackage(const QString &p)
+{
+ S_D(TypeSystemTypeEntry);
+ d->m_docTargetLangPackage = p;
+}
+
+TypeSystem::DocMode TypeSystemTypeEntry::docMode() const
+{
+ S_D(const TypeSystemTypeEntry);
+ return d->m_docMode;
+}
+
+void TypeSystemTypeEntry::setDocMode(TypeSystem::DocMode m)
+{
+ S_D(TypeSystemTypeEntry);
+ d->m_docMode = m;
+}
+
const QString &TypeSystemTypeEntry::namespaceBegin() const
{
S_D(const TypeSystemTypeEntry);
QString m_defaultConstructor;
QString m_defaultSuperclass;
QString m_qualifiedCppName;
+ QString m_docFile;
uint m_polymorphicBase : 1;
uint m_genericClass : 1;
d->m_isValueTypeWithCopyConstructorOnly = v;
}
+QString ComplexTypeEntry::docFile() const
+{
+ S_D(const ComplexTypeEntry);
+ return d->m_docFile;
+}
+
+void ComplexTypeEntry::setDocFile(const QString &docFile)
+{
+ S_D(ComplexTypeEntry);
+ d->m_docFile = docFile;
+}
+
// FIXME PYSIDE 7: Remove this and make "true" the default
static bool parentManagementEnabled = false;
DocModificationXPathReplace
};
+enum class DocMode {
+ Nested,
+ Flat
+};
+
enum class ExceptionHandling {
Unspecified,
Off,
#include "containertypeentry.h"
#include "customconversion.h"
#include "customtypenentry.h"
+#include "documentation_enums.h"
#include "flagstypeentry.h"
#include "functiontypeentry.h"
#include "namespacetypeentry.h"
constexpr auto overloadNumberAttribute = "overload-number"_L1;
constexpr auto ownershipAttribute = "owner"_L1;
constexpr auto packageAttribute = "package"_L1;
+constexpr auto docPackageAttribute = "doc-package"_L1;
+constexpr auto docModeAttribute = "doc-mode"_L1;
constexpr auto polymorphicBaseAttribute = "polymorphic-base"_L1;
constexpr auto positionAttribute = "position"_L1;
constexpr auto preferredConversionAttribute = "preferred-conversion"_L1;
};
ENUM_LOOKUP_LINEAR_SEARCH
+ENUM_LOOKUP_BEGIN(TypeSystem::DocMode, Qt::CaseSensitive,
+ docModeFromAttribute)
+{
+ {u"nested", TypeSystem::DocMode::Nested},
+ {u"flat", TypeSystem::DocMode::Flat},
+};
+ENUM_LOOKUP_LINEAR_SEARCH
+
ENUM_LOOKUP_BEGIN(TypeSystem::QtMetaTypeRegistration, Qt::CaseSensitive,
qtMetaTypeFromAttribute)
{
};
ENUM_LOOKUP_LINEAR_SEARCH
+ENUM_LOOKUP_BEGIN(DocumentationFormat, Qt::CaseInsensitive,
+ documentationFormatFromAttribute)
+ {
+ {u"native", DocumentationFormat::Native},
+ {u"target", DocumentationFormat::Target}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
+ENUM_LOOKUP_BEGIN(DocumentationEmphasis, Qt::CaseSensitive,
+ documentationEmphasisFromAttribute)
+ {
+ {u"none", DocumentationEmphasis::None},
+ {u"language-note", DocumentationEmphasis::LanguageNote}
+ };
+ENUM_LOOKUP_LINEAR_SEARCH
+
ENUM_LOOKUP_BEGIN(TypeSystem::Ownership, Qt::CaseInsensitive,
ownershipFromFromAttribute)
{
"and return types should not be part of the signature.")
.arg(tag, signature);
}
- return QString();
+ return {};
}
inline TypeEntryCPtr TypeSystemParser::currentParentTypeEntry() const
if (convertBoolean(attribute.value(), parentManagementAttribute, false))
ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::ParentManagement);
ComplexTypeEntry::setParentManagementEnabled(true);
+ } else if (name == docFileAttribute) {
+ ctype->setDocFile(attributes->takeAt(i).value().toString());
}
}
}
TypeSystem::DocModificationMode mode = TypeSystem::DocModificationReplace;
- TypeSystem::Language lang = TypeSystem::NativeCode;
+ DocumentationFormat format = DocumentationFormat::Native;
+ DocumentationEmphasis emphasis = DocumentationEmphasis::None;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
if (name == u"mode") {
mode = modeOpt.value();
} else if (name == formatAttribute) {
const auto attribute = attributes->takeAt(i);
- const auto langOpt = languageFromAttribute(attribute.value());
- if (!langOpt.has_value()) {
+ const auto formatOpt = documentationFormatFromAttribute(attribute.value());
+ if (!formatOpt.has_value()) {
m_error = msgInvalidAttributeValue(attribute);
return false;
}
- lang = langOpt.value();
+ format = formatOpt.value();
+ } else if (name == u"emphasis") {
+ const auto attribute = attributes->takeAt(i);
+ const auto emphasisOpt = documentationEmphasisFromAttribute(attribute.value());
+ if (!emphasisOpt.has_value()) {
+ m_error = msgInvalidAttributeValue(attribute);
+ return false;
+ }
+ emphasis = emphasisOpt.value();
}
}
+ if (emphasis != DocumentationEmphasis::None && mode == TypeSystem::DocModificationXPathReplace) {
+ m_error = "Emphasis is not supported for XPathReplace"_L1;
+ return false;
+ }
+
QString signature = isTypeEntry(topElement) ? QString() : m_currentSignature;
DocModification mod(mode, signature);
- mod.setFormat(lang);
+ mod.setFormat(format);
+ mod.setEmphasis(emphasis);
if (hasFileSnippetAttributes(attributes)) {
const auto snippetOptional = readFileSnippet(attributes);
if (!snippetOptional.has_value())
QString subModuleOf;
QString namespaceBegin;
QString namespaceEnd;
+ QString docPackage;
+ std::optional<TypeSystem::DocMode> docModeOpt;
for (auto i = attributes->size() - 1; i >= 0; --i) {
const auto name = attributes->at(i).qualifiedName();
if (name == packageAttribute) {
m_defaultPackage = attributes->takeAt(i).value().toString();
+ } else if (name == docPackageAttribute) {
+ docPackage = attributes->takeAt(i).value().toString();
+ } else if (name == docModeAttribute) {
+ const auto attribute = attributes->takeAt(i);
+ docModeOpt = docModeFromAttribute(attribute.value());
+ if (!docModeOpt.has_value()) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgInvalidAttributeValue(attribute)));
+ }
} else if (name == defaultSuperclassAttribute) {
m_defaultSuperclass = attributes->takeAt(i).value().toString();
} else if (name == exceptionHandlingAttribute) {
currentParentTypeEntry()));
moduleEntry->setSubModule(subModuleOf);
}
+ if (!docPackage.isEmpty())
+ moduleEntry->setDocTargetLangPackage(docPackage);
+ if (docModeOpt.has_value())
+ moduleEntry->setDocMode(docModeOpt.value());
moduleEntry->setCodeGeneration(m_generate);
moduleEntry->setSnakeCase(snakeCase);
if (!namespaceBegin.isEmpty())
static bool parseOverloadNumber(const QXmlStreamAttribute &attribute, int *overloadNumber,
QString *errorMessage)
{
- bool ok;
+ bool ok{};
*overloadNumber = attribute.value().toInt(&ok);
if (!ok || *overloadNumber < 0) {
*errorMessage = msgInvalidAttributeValue(attribute);
const auto snippetOptional = readFileSnippet(attributes);
if (!snippetOptional.has_value())
return false;
- const auto snippet = snippetOptional.value();
+ const auto &snippet = snippetOptional.value();
QString source = snippet.fileName;
if (!snippet.snippetLabel.isEmpty())
QString subModuleOf() const;
void setSubModule(const QString &);
+ bool hasDocTargetLangPackage() const;
+ QString docTargetLangPackage() const;
+ void setDocTargetLangPackage(const QString &p);
+ TypeSystem::DocMode docMode() const;
+ void setDocMode(TypeSystem::DocMode m);
+
const QString &namespaceBegin() const;
void setNamespaceBegin(const QString &n);
#if defined(HAVE_LIBXSLT)
return libXml_createXQuery(focus, errorMessage);
#else
+ Q_UNUSED(focus);
*errorMessage = QLatin1StringView(__FUNCTION__) + u" is not implemented."_s;
- return std::shared_ptr<XQuery>();
+ return {};
#endif
}
#if defined(HAVE_LIBXSLT)
return libXslt_transform(xml, xsl, errorMessage);
#else
+ Q_UNUSED(xsl);
*errorMessage = QLatin1StringView(__FUNCTION__) + u" is not implemented."_s;
return xml;
#endif
enable_testing()
add_subdirectory(tests)
endif()
-
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
-
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
-
else()
execute_process(
COMMAND ${Python_EXECUTABLE} -c "if True:
+ import sys
import sysconfig
from os.path import sep
# /home/qt/dev/env/lib/python3.9/site-packages
- lib_path = sysconfig.get_path('purelib')
+ if sys.platform == 'win32':
+ lib_path = sysconfig.get_path('purelib')
+ else:
+ lib_path = sysconfig.get_path('purelib', scheme='posix_prefix')
# /home/qt/dev/env
- data_path = sysconfig.get_path('data')
+ if sys.platform == 'win32':
+ data_path = sysconfig.get_path('data')
+ else:
+ data_path = sysconfig.get_path('data', scheme='posix_prefix')
# /lib/python3.9/site-packages
rel_path = lib_path.replace(data_path, '')
find_package(Clang CONFIG REQUIRED)
# Need to explicitly handle the version check, because the Clang package doesn't.
- if (LLVM_PACKAGE_VERSION AND LLVM_PACKAGE_VERSION VERSION_LESS "9.0")
- message(FATAL_ERROR "You need LLVM version 9.0 or greater to build.")
+ set(REQUIRED_LLVM "18.0")
+
+ if (LLVM_PACKAGE_VERSION AND LLVM_PACKAGE_VERSION VERSION_LESS "${REQUIRED_LLVM}")
+ message(WARNING "You need LLVM version ${REQUIRED_LLVM} or greater to build PySide "
+ "without issues, and ${LLVM_PACKAGE_VERSION} was found. "
+ "A lower version might case problems, specially on Windows.")
+ # Exception to enable Yocto builds (Kirkstone) - 6.8.x
+ set(REQUIRED_LLVM "14.0")
+ if (LLVM_PACKAGE_VERSION AND LLVM_PACKAGE_VERSION VERSION_LESS "${REQUIRED_LLVM}")
+ message(FATAL_ERROR "Using a LLVM version ${REQUIRED_LLVM} is the minimum allowed "
+ "to work pyside in some systems, however ${LLVM_PACKAGE_VERSION} was found.")
+ endif()
endif()
# CLANG_LIBRARY is read out from the cmake cache to deploy libclang
# Python_SOABI is only set by CMake 3.17+
# TODO: Lower this to CMake 3.16 if possible.
if(SHIBOKEN_IS_CROSS_BUILD)
- # For android platform armv7a FindPython module return Python_SOABI as empty because
- # it is unable to set Python_CONFIG i.e. find `python3-config` script
- # This workaround sets the Python_SOABI manually for this platform.
- if(CMAKE_SYSTEM_NAME STREQUAL "Android" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "armv7-a")
- set(Python_SOABI "cpython-311}")
- endif()
if(NOT Python_SOABI)
message(FATAL_ERROR "Python_SOABI variable is empty.")
endif()
"${_shiboken_backup_CMAKE_FIND_ROOT_PATH_MODE_PROGRAM}")
set(CMAKE_FIND_ROOT_PATH
"${_shiboken_backup_CMAKE_FIND_ROOT_PATH}")
+
+ # For Android platform sometimes the FindPython module returns Python_SOABI as empty in
+ # certain scenarios eg: armv7a target, macOS host etc. This is because
+ # it is unable to set Python_CONFIG i.e. `python3-config` script
+ # This workaround sets the Python_SOABI manually for this Android platform.
+ # This needs to be updated manually if the Python version for Android cross compilation
+ # changes.
+ # TODO: Find a better way to set Python_SOABI for Android platform
+ if(CMAKE_SYSTEM_NAME STREQUAL "Android" AND NOT Python_SOABI)
+ set(Python_SOABI "cpython-311")
+ endif()
else()
find_package(
Python
endmacro()
macro(shiboken_validate_python_version)
- if(Python_VERSION_MAJOR EQUAL "3" AND Python_VERSION_MINOR LESS "7")
+ if(Python_VERSION_MAJOR EQUAL "3" AND Python_VERSION_MINOR LESS "8")
message(FATAL_ERROR
- "Shiboken requires Python 3.7+.")
+ "Shiboken requires Python 3.8+.")
endif()
endmacro()
set(debug_level "")
if(SHIBOKEN_DEBUG_LEVEL)
set(debug_level "--debug-level=${SHIBOKEN_DEBUG_LEVEL}")
- elseif(DEFINED $ENV{SHIBOKEN_DEBUG_LEVEL})
+ elseif(DEFINED ENV{SHIBOKEN_DEBUG_LEVEL})
set(debug_level "--debug-level=$ENV{SHIBOKEN_DEBUG_LEVEL}")
endif()
set(${out_var} "${debug_level}" PARENT_SCOPE)
message(STATUS "qfp:python_info:config_dir: ${_${_PYTHON_PREFIX}_CONFIGDIR}")
endif()
endif()
-
message(STATUS "qfp:qt_info:QT_INSTALL_TESTS: ${QT6_INSTALL_PREFIX}/${QT6_INSTALL_TESTS}")
message(STATUS "qfp:qt_info:QT_INSTALL_TRANSLATIONS: ${QT6_INSTALL_PREFIX}/${QT6_INSTALL_TRANSLATIONS}")
message(STATUS "qfp:qt_info:is_shared: ${QT6_IS_SHARED_LIBS_BUILD}")
-
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/shiboken6${shiboken6_SUFFIX}.pc"
DESTINATION "${LIB_INSTALL_DIR}/pkgconfig")
-
.SH AUTHORS
Lauro Moura <lauro.neto at openbossa dot org>, Bruno Araujo <bruno.araujo at openbossa dot org>, Hugo Lima <hugo.lima at openbossa dot org>
-
.SH AUTHORS
Lauro Moura <lauro.neto at openbossa dot org>, Bruno Araujo <bruno.araujo at openbossa dot org>, Hugo Lima <hugo.lima at openbossa dot org>
-
Version: @generatorrunner_VERSION@
Libs: -L${libdir} -lgenrunner@generator_SUFFIX@
Cflags: -I${includedir}@generator_SUFFIX@
-
font-size: +1rem;
color: var(--color-brand-primary);
}
+
+article[role="main"] section[id^="pyside6-"] h1 {
+ font-size: 1.5em;
+}
+
+article[role="main"] section[id^="pyside6-"] dl dt em span.pre{
+ font-size: 1.5em;
+}
+
+article[role="main"] section[id^="pyside6-"] dl dt span.pre {
+ font-size: 1.5em;
+}
.. grid-item-card:: Scriptable Application
:class-item: cover-img
:link: ../../examples/example_scriptableapplication_scriptableapplication.html
- :img-top: ../../../_images/example_no_image.png
+ :img-top: ../../../pyside6/doc/example_no_image.png
.. grid-item-card:: Widget Binding
:class-item: cover-img
:link: ../../examples/example_widgetbinding_widgetbinding.html
- :img-top: ../../../_images/example_no_image.png
+ :img-top: ../../../pyside6/doc/example_no_image.png
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import fileinput
import re
well to prevent the headers from being suppressed in the generated
code.
-::ref:`typesystem`: XML files that provides the developer with a tool to customize the
+::ref:`typesystem_details`: XML files that provides the developer with a tool to customize the
way that the generators will see the classes and functions. For
example, functions can be renamed, have its signature changed and
many other actions.
.. code-block:: ini
VALUE-ARGUMENT = VALUE
-
-.. module:: Shiboken
+.. py:module:: Shiboken
-.. |maya| unicode:: Maya U+2122
+ .. |maya| unicode:: Maya U+2122
-.. _shiboken-module:
+ .. _shiboken-module:
-Shiboken module
-***************
+ Shiboken module
+ ***************
-Functions
-^^^^^^^^^
+ Functions
+ ^^^^^^^^^
-.. container:: function_list
+ .. container:: function_list
- * def :meth:`isValid<shiboken.isValid>` (obj)
- * def :meth:`wrapInstance<shiboken.wrapInstance>` (address, type)
- * def :meth:`getCppPointer<shiboken.getCppPointer>` (obj)
- * def :meth:`delete<shiboken.delete>` (obj)
- * def :meth:`isOwnedByPython<shiboken.isOwnedByPython>` (obj)
- * def :meth:`wasCreatedByPython<shiboken.wasCreatedByPython>` (obj)
- * def :meth:`dump<shiboken.dump>` (obj)
- * def :meth:`disassembleFrame<shiboken.disassembleFrame>` (marker)
+ * :py:func:`isValid`
+ * :py:func:`wrapInstance`
+ * :py:func:`getCppPointer`
+ * :py:func:`delete`
+ * :py:func:`ownedByPython`
+ * :py:func:`createdByPython`
+ * :py:func:`dump`
+ * :py:func:`disassembleFrame`
+ * :py:func:`dumpTypeGraph`
+ * :py:func:`dumpWrapperMap`
+ * :py:func:`dumpConverters`
-Detailed description
-^^^^^^^^^^^^^^^^^^^^
+ Classes
+ ^^^^^^^
-This Python module can be used to access internal information related to our
-binding technology. Access to this internal information is required to e.g.:
-integrate PySide with Qt based programs that offer Python scripting like |maya|
-or just for debug purposes.
+ .. container:: class_list
-Some function description refer to "Shiboken based objects", wich means
-Python objects instances of any Python Type created using Shiboken.
+ * :py:class:`VoidPtr`
-To import the module:
+ Detailed description
+ ^^^^^^^^^^^^^^^^^^^^
-.. code-block:: python
+ This Python module can be used to access internal information related to our
+ binding technology. Access to this internal information is required to e.g.:
+ integrate PySide with Qt based programs that offer Python scripting like |maya|
+ or just for debug purposes.
- from shiboken6 import Shiboken
+ Some function description refer to "Shiboken based objects", which means
+ Python objects instances of any Python Type created using Shiboken.
-.. function:: isValid(obj)
+ To import the module:
- Given a Python object, returns True if the object methods can be called
- without an exception being thrown. A Python wrapper becomes invalid when
- the underlying C++ object is destroyed or unreachable.
+ .. code-block:: python
-.. function:: wrapInstance(address, type)
+ from shiboken6 import Shiboken
- Creates a Python wrapper for a C++ object instantiated at a given memory
- address - the returned object type will be the same given by the user.
+ .. py:function:: isValid(obj: object) -> bool
- The type must be a Shiboken type, the C++ object will not be
- destroyed when the returned Python object reach zero references.
+ Given a Python object, returns True if the object methods can be called
+ without an exception being thrown. A Python wrapper becomes invalid when
+ the underlying C++ object is destroyed or unreachable.
- If the address is invalid or doesn't point to a C++ object of given type
- the behavior is undefined.
+ :param obj: Python object to validate.
-.. function:: getCppPointer(obj)
+ .. py:function:: wrapInstance(address: int, python_type: type) -> Shiboken.object
- Returns a tuple of longs that contain the memory addresses of the
- C++ instances wrapped by the given object.
+ Creates a Python wrapper for a C++ object instantiated at a given memory
+ address - the returned object type will be the same given by the user.
-.. function:: delete(obj)
+ The type must be a Python type. The C++ object will not be
+ destroyed when the returned Python object reach zero references.
- Deletes the C++ object wrapped by the given Python object.
+ If the address is invalid or doesn't point to a C++ object of given type
+ the behavior is undefined.
-.. function:: isOwnedByPython(obj)
+ :param int address: Address of the C++ object.
+ :param type python_type: Python type for the corresponding C++ object.
- Given a Python object, returns True if Python is responsible for deleting
- the underlying C++ object, False otherwise.
+ .. py:function:: getCppPointer(obj: Shiboken.object) -> tuple[int, ...]
- If the object was not a Shiboken based object, a TypeError is
- thrown.
+ Returns a tuple of longs that contain the memory addresses of the
+ C++ instances wrapped by the given object.
-.. function:: wasCreatedByPython(obj)
+ :param obj: Shiboken object.
- Returns true if the given Python object was created by Python.
+ .. py:function:: delete(obj: Shiboken.object)
-.. function:: dump(obj)
+ Deletes the C++ object wrapped by the given Python object.
- Returns a string with implementation-defined information about the
- object.
- This method should be used **only** for debug purposes by developers
- creating their own bindings as no guarantee is provided that
- the string format will be the same across different versions.
+ :param obj: Shiboken object.
- If the object is not a Shiboken based object, a message is printed.
+ .. py:function:: ownedByPython(obj: Shiboken.object) -> bool
-.. function:: disassembleFrame(label)
+ Given a Shiboken object, returns True if Python is responsible for deleting
+ the underlying C++ object, False otherwise.
- Prints the current executing Python frame to stdout and flushes.
- The disassembly is decorated by some label. Example:
+ If the object was not a Shiboken based object, a TypeError is
+ thrown.
- .. code-block:: python
+ :param obj: Shiboken object.
+
+ .. py:function:: createdByPython(obj: Shiboken.object) -> bool
+
+ Returns true if the given Python object was created by Python.
+
+ :param obj: Shiboken object.
+
+ .. py:function:: dump(obj: object) -> str
+
+ Returns a string with implementation-defined information about the
+ object.
+ This method should be used **only** for debug purposes by developers
+ creating their own bindings as no guarantee is provided that
+ the string format will be the same across different versions.
+
+ If the object is not a Shiboken based object, a message is printed.
+
+ :param obj: Python object.
+
+ .. py:function:: disassembleFrame(label: str)
+
+ Prints the current executing Python frame to stdout and flushes.
+ The disassembly is decorated by some label. Example:
+
+ .. code-block:: python
+
+ lambda: 42
+
+ is shown from inside C++ as
+
+ .. code-block:: c
+
+ <label> BEGIN
+ 1 0 LOAD_CONST 1 (42)
+ 2 RETURN_VALUE
+ <label> END
- lambda: 42
+ When you want to set a breakpoint at the `disassembleFrame` function
+ and you use it from C++, you use the pure function name.
- is shown from inside C++ as
+ When you want to use it from Python, you can insert it into your Python
+ code and then maybe instead set a breakpoint at `SbkShibokenModule_disassembleFrame`
+ which is the generated wrapper.
- .. code-block:: c
+ `label` is a simple string in C++. In Python, you can use any object;
+ internally the `str` function is called with it.
- <label> BEGIN
- 1 0 LOAD_CONST 1 (42)
- 2 RETURN_VALUE
- <label> END
+ This method should be used **only** for debug purposes by developers.
- When you want to set a breakpoint at the `disassembleFrame` function
- and you use it from C++, you use the pure function name.
+ :param label: Python string.
- When you want to use it from Python, you can insert it into your Python
- code and then maybe instead set a breakpoint at `SbkShibokenModule_disassembleFrame`
- which is the generated wrapper.
+ .. py:function:: dumpTypeGraph(file_name: str) -> bool
- `label` is a simple string in C++. In Python, you can use any object;
- internally the `str` function is called with it.
+ Dumps the inheritance graph of the types existing in libshiboken
+ to ``.dot`` file for use with `Graphviz <https://graphviz.org/>`_.
- This method should be used **only** for debug purposes by developers.
+ :param file_name: Name of the file to write the graph.
- .. function:: dumpTypeGraph(file_name)
+ .. py:function:: dumpWrapperMap()
- Dumps the inheritance graph of the types existing in libshiboken
- to ``.dot`` file for use with `Graphviz <https://graphviz.org/>`_.
+ Dumps the map of wrappers existing in libshiboken to standard error.
-.. function:: dumpWrapperMap()
+ .. py:function:: dumpConverters()
- Dumps the map of wrappers existing in libshiboken to standard error.
+ Dumps the map of named converters existing in libshiboken to standard
+ error.
- .. py:class:: VoidPtr(address, size = -1, writeable = 0)
+ .. py:class:: VoidPtr(address, size = -1, writeable = 0)
- :param address: (PyBuffer, SbkObject, int, VoidPtr)
- :param size: int
- :param writeable: int
+ :param address: (PyBuffer, SbkObject, int, VoidPtr)
+ :param size: int
+ :param writeable: int
- Represents a chunk of memory by address and size and implements the ``buffer`` protocol.
- It can be constructed from a ``buffer``, a Shiboken based object, a memory address
- or another VoidPtr instance.
+ Represents a chunk of memory by address and size and implements the ``buffer`` protocol.
+ It can be constructed from a ``buffer``, a Shiboken based object, a memory address
+ or another VoidPtr instance.
- .. py:method:: toBytes()
+ .. py:method:: toBytes()
- :rtype: bytes
+ :rtype: bytes
- Returns the contents as ``bytes``.
+ Returns the contents as ``bytes``.
.. toctree::
:maxdepth: 1
- typesystem_arguments.rst
+ Function argument modifications <typesystem_arguments.rst>
typesystem_codeinjection.rst
typesystem_converters.rst
typesystem_containers.rst
typesystem_templates.rst
- typesystem_modify_function.rst
typesystem_manipulating_objects.rst
typesystem_conversionrule.rst
typesystem_documentation.rst
-.. _modifying-arguments:
+.. _modify-argument:
-Modifying Arguments
--------------------
+modify-argument
+---------------
-.. _conversionrule-on-arguments:
-
-conversion-rule
-^^^^^^^^^^^^^^^
-
-The ``conversion-rule`` node allows you to write customized code to convert
-the given argument between the target language and C++.
-It is then a child of the :ref:`modify-argument` node:
+Function argument modifications consist of a list of ``modify-argument`` nodes
+contained in :ref:`modify-function`, :ref:`add-function` or
+:ref:`declare-function` nodes. Nested :ref:`remove-argument`,
+:ref:`replace-default-expression`, :ref:`remove-default-expression`,
+:ref:`replace-type`, :ref:`reference-count` and :ref:`define-ownership`
+nodes specify the details of the modification.
.. code-block:: xml
- <modify-argument index="2">
- <!-- for the second argument of the function -->
- <conversion-rule class="target | native">
- // the code
- </conversion-rule>
- </modify-argument>
+ <modify-function>
+ <modify-argument index="return | this | 1 ..." rename="..."
+ invalidate-after-use = "true | false" pyi-type="...">
+ // modifications
+ </modify-argument>
+ </modify-function>
-The ``class`` attribute accepts one of the following values to define the
-conversion direction to be either ``target-to-native`` or ``native-to-target``:
+Set the ``index`` attribute to "1" for the first argument, "2" for the second
+one and so on. Alternatively, set it to "return" or "this" if you want to
+modify the function's return value or the object the function is called upon,
+respectively.
-* ``native``: Defines the conversion direction to be ``target-to-native``.
- It is similar to the existing ``<target-to-native>`` element.
- See :ref:`Conversion Rule Tag <conversion-rule-tag>` for more information.
-
-* ``target``: Defines the conversion direction to be ``native-to-target``.
- It is similar to the existing ``<native-to-target>`` element.
- See :ref:`Conversion Rule Tag <conversion-rule-tag>` for more information.
-
-This node is typically used in combination with the :ref:`replace-type` and
-:ref:`remove-argument` nodes. The given code is used instead of the generator's
-conversion code.
+The optional ``rename`` attribute is used to rename a argument and use this
+new name in the generated code. This attribute can be used to enable the usage
+of ``keyword arguments``.
-Writing %N in the code (where N is a number), will insert the name of the
-nth argument. Alternatively, %in and %out which will be replaced with the
-name of the conversion's input and output variable, respectively. Note the
-output variable must be declared explicitly, for example:
-
-.. code-block:: xml
-
- <conversion-rule class="native">
- bool %out = (bool) %in;
- </conversion-rule>
+The optional ``pyi-type`` attribute specifies the type to appear in the
+signature strings and ``.pyi`` files. The type string is determined by
+checking this attribute value, the :ref:`replace-type` modification and
+the C++ type. The attribute can be used for example to enclose
+a pointer return value within ``Optional[]`` to indicate that ``None``
+can occur.
-.. note::
+For the optional ``invalidate-after-use`` attribute,
+see :ref:`invalidationafteruse` .
- You can also use the ``conversion-rule`` node to specify
- :ref:`a conversion code which will be used instead of the generator's conversion code everywhere for a given type <conversion-rule-tag>`.
+Naming, type, default value modifications
++++++++++++++++++++++++++++++++++++++++++
.. _remove-argument:
the fully qualified name (including name of the package as well as the class
name).
+Ownership/Reference modifications
++++++++++++++++++++++++++++++++++
+
.. _define-ownership:
define-ownership
The ``define-ownership`` tag indicates that the function changes the ownership
rules of the argument object, and it is a child of the
:ref:`modify-argument` node.
+
+.. code-block:: xml
+
+ <modify-argument>
+ <define-ownership class="target | native"
+ owner="target | c++ | default" />
+ </modify-argument>
+
The ``class`` attribute specifies the class of
function where to inject the ownership altering code
(see :ref:`codegenerationterminology`). The ``owner`` attribute
specifies the new ownership of the object. It accepts the following values:
* target: the target language will assume full ownership of the object.
- The native resources will be deleted when the target language
- object is finalized.
+ The native resources will be deleted when the target language
+ object is finalized.
* c++: The native code assumes full ownership of the object. The target
- language object will not be garbage collected.
+ language object will not be garbage collected.
* default: The object will get default ownership, depending on how it
- was created.
-
-.. code-block:: xml
-
- <modify-argument>
- <define-ownership class="target | native"
- owner="target | c++ | default" />
- </modify-argument>
+ was created.
.. _reference-count:
The ``reference-count`` tag dictates how an argument should be handled by the
target language reference counting system (if there is any), it also indicates
the kind of relationship the class owning the function being modified has with
-the argument. It is a child of the :ref:`modify-argument` node.
-For instance, in a model/view relation a view receiving a model
-as argument for a **setModel** method should increment the model's reference
-counting, since the model should be kept alive as much as the view lives.
-Remember that out hypothetical view could not become parent of the model,
-since the said model could be used by other views as well.
-The ``action`` attribute specifies what should be done to the argument
-reference counting when the modified method is called. It accepts the
-following values:
-
-* add: increments the argument reference counter.
-* add-all: increments the reference counter for each item in a collection.
-* remove: decrements the argument reference counter.
-* set: will assign the argument to the variable containing the reference.
-* ignore: does nothing with the argument reference counter
- (sounds worthless, but could be used in situations
- where the reference counter increase is mandatory
- by default).
+the argument (represented as lists of referred-to objects stored in the
+owner class). It is a child of the :ref:`modify-argument` node.
.. code-block:: xml
<modify-argument>
- <reference-count action="add|add-all|remove|set|ignore" variable-name="..." />
+ <reference-count action="add|remove|set|ignore" variable-name="..." />
</modify-argument>
+The ``action`` attribute specifies what should be done to the argument
+reference counting when the modified method is called. It accepts the
+following values:
-The variable-name attribute specifies the name used for the variable that
-holds the reference(s).
-
-.. _replace-value:
-
-replace-value
-^^^^^^^^^^^^^
-
-The ``replace-value`` attribute lets you replace the return statement of a
-function with a fixed string. This attribute can only be used for the
-argument at ``index`` 0, which is always the function's return value.
+* add: Adds the argument to the list of previous argument values stored
+ under this ``variable-name`` or function signature and increments
+ the argument reference counter.
+* remove: Decrements the argument reference counter and removes it from
+ the list of argument values stored under this ``variable-name``
+ or function signature.
+* set: Decreases the reference count of the previously stored argument values
+ under this ``variable-name`` or function signature and removes them.
+ Stores the argument and increments the argument reference counter.
+* ignore: does nothing with the argument reference counter
+ (sounds worthless, but could be used in situations
+ where the reference counter increase is mandatory by default).
-.. code-block:: xml
+The ``variable-name`` attribute specifies the name used for the variable that
+holds the reference(s). It defaults to the function signature.
- <modify-argument index="0" replace-value="this"/>
+For instance, in a model/view relation, a view receiving a model
+as argument for a **setModel()** method should increment the model's reference
+counting, since the model should be kept alive as long as the view lives.
+Remember that our hypothetical view cannot become a :ref:`parent` of the
+model, since the said model could be used by other views as well.
.. _parent:
In the ``index`` argument you must specify the parent argument. The action
*add* creates a parent link between objects, while *remove* will undo the
parentage relationship.
+
+Other modifications
++++++++++++++++++++
+
+.. _conversionrule-on-arguments:
+
+conversion-rule
+^^^^^^^^^^^^^^^
+
+The ``conversion-rule`` node allows you to write customized code to convert
+the given argument between the target language and C++.
+It is then a child of the :ref:`modify-argument` node:
+
+.. code-block:: xml
+
+ <modify-argument index="2">
+ <!-- for the second argument of the function -->
+ <conversion-rule class="target | native">
+ // the code
+ </conversion-rule>
+ </modify-argument>
+
+The ``class`` attribute accepts one of the following values to define the
+conversion direction to be either ``target-to-native`` or ``native-to-target``:
+
+* ``native``: Defines the conversion direction to be ``target-to-native``.
+ It is similar to the existing ``<target-to-native>`` element.
+ See :ref:`Conversion Rule Tag <conversion-rule-tag>` for more information.
+
+* ``target``: Defines the conversion direction to be ``native-to-target``.
+ It is similar to the existing ``<native-to-target>`` element.
+ See :ref:`Conversion Rule Tag <conversion-rule-tag>` for more information.
+
+This node is typically used in combination with the :ref:`replace-type` and
+:ref:`remove-argument` nodes. The given code is used instead of the generator's
+conversion code.
+
+Writing %N in the code (where N is a number), will insert the name of the
+nth argument. Alternatively, %in and %out which will be replaced with the
+name of the conversion's input and output variable, respectively. Note the
+output variable must be declared explicitly, for example:
+
+.. code-block:: xml
+
+ <conversion-rule class="native">
+ bool %out = (bool) %in;
+ </conversion-rule>
+
+.. note::
+
+ You can also use the ``conversion-rule`` node to specify
+ :ref:`a conversion code which will be used instead of the generator's conversion code everywhere for a given type <conversion-rule-tag>`.
+
+.. _replace-value:
+
+replace-value
+^^^^^^^^^^^^^
+
+The ``replace-value`` attribute lets you replace the return statement of a
+function with a fixed string. This attribute can only be used for the
+argument at ``index`` 0, which is always the function's return value.
+
+.. code-block:: xml
+
+ <modify-argument index="0" replace-value="this"/>
The value **Target** means the modification is applied to the
**Python Wrapper**. The value **Native** means the modification is applied to
the **C++ Wrapper**.
-
demonstrates an example of using an opaque container `QPointList`, which wraps a C++
`QList<QPoint>`. The corresponding typesystem file where QPointList can be found `here
<https://code.qt.io/cgit/pyside/pyside-setup.git/tree/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml>`_
-
the generator as in the ``native-to-target`` tag.
The ``file`` and ``snippet`` attributes are also supported (see :ref:`inject-code` nodes).
-
<value-type>
<inject-documentation mode="append | prepend | replace" format="native | target"
+ emphasis="none | language-note"
file="[file]" snippet="[label]">
// the documentation
</inject-code>
* native: Before XML<->Backend transformation occur, so the injected code *must* be a valid XML.
* target: After XML<->Backend transformation occur, so the injected code *must* be a valid backend format.
+The optional **emphasis** attribute enables emphasizing the documentation snippet.
+The value ``language-note`` encloses it in a custom Sphinx admonition entitled
+*Python Language Note*. The text should be a plain paragraph for this to work.
+
The optional ``file`` attribute specifies the file name
(see :ref:`external-snippets`).
The ``modify-function`` node allows you to modify a given C++ function when
mapping it onto the target language, and it is a child of a :ref:`function`,
:ref:`namespace`, :ref:`object-type` or a :ref:`value-type` node.
-Use the :ref:`modify-argument` node to specify which argument the
-modification affects.
+Nested :ref:`modify-argument` nodes can used to modify arguments
+or return values.
.. code-block:: xml
The ``add-function`` node allows you to add a given function onto the target
language, and it is a child of an :ref:`object-type` or :ref:`value-type` nodes if the
-function is supposed to be a method, or :ref:`namespace` and :ref:`typesystem` if
+function is supposed to be a method, or :ref:`namespace` and :ref:`typesystem_details` if
the function is supposed to be a function inside a namespace or a global function.
It may contain :ref:`modify-argument` nodes.
The ``declare-function`` node allows you to declare a function present in
the type and it is a child of an :ref:`object-type` or :ref:`value-type` nodes
if the function is supposed to be a method, or :ref:`namespace` and
-:ref:`typesystem` if the function is supposed to be a function inside a
+:ref:`typesystem_details` if the function is supposed to be a function inside a
namespace or a global function. It may contain :ref:`modify-argument` nodes.
.. code-block:: xml
+++ /dev/null
-.. _modifying-functions:
-
-Modifying Functions
--------------------
-
-.. _modify-argument:
-
-modify-argument
-^^^^^^^^^^^^^^^
-
-Function modifications consist of a list of ``modify-argument`` nodes
-contained in :ref:`modify-function`, :ref:`add-function` or
-:ref:`declare-function` nodes. Use the :ref:`remove-argument`,
-:ref:`replace-default-expression`, :ref:`remove-default-expression`,
-:ref:`replace-type`, :ref:`reference-count` and :ref:`define-ownership`
-nodes to specify the details of the modification.
-
-.. code-block:: xml
-
- <modify-function>
- <modify-argument index="return | this | 1 ..." rename="..."
- invalidate-after-use = "true | false" pyi-type="...">
- // modifications
- </modify-argument>
- </modify-function>
-
-Set the ``index`` attribute to "1" for the first argument, "2" for the second
-one and so on. Alternatively, set it to "return" or "this" if you want to
-modify the function's return value or the object the function is called upon,
-respectively.
-
-The optional ``rename`` attribute is used to rename a argument and use this
-new name in the generated code. This attribute can be used to enable the usage
-of ``keyword arguments``.
-
-The optional ``pyi-type`` attribute specifies the type to appear in the
-signature strings and ``.pyi`` files. The type string is determined by
-checking this attribute value, the :ref:`replace-type` modification and
-the C++ type. The attribute can be used for example to enclose
-a pointer return value within ``Optional[]`` to indicate that ``None``
-can occur.
-
-For the optional ``invalidate-after-use`` attribute,
-see :ref:`invalidationafteruse` .
The generator will generate several warnings which may be irrelevant to the
user. The ``suppress-warning`` node suppresses the specified warning, and it is
-a child of the :ref:`typesystem` node.
+a child of the :ref:`typesystem_details` node.
.. code-block:: xml
The ``extra-includes`` node contains declarations of additional include files,
and it can be a child of the :ref:`namespace`, :ref:`value-type`,
-:ref:`object-type` and :ref:`typesystem` and nodes.
+:ref:`object-type` and :ref:`typesystem_details` and nodes.
The generator automatically tries to read the global header for each type but
sometimes it is required to include extra files in the generated C++ code to
*local* means that the file is in a local directory and will be included
using #include "...".
-When specified as a child of the :ref:`typesystem` node, the include
+When specified as a child of the :ref:`typesystem_details` node, the include
directives are added to the module source file which contains
the type converter and registration code. It can be used to specify
additional includes required for the converter code snippets.
Note that this is not a standard externally parsed entity due to the limitations
of the underlying parser.
-.. _typesystem:
+.. _typesystem_details:
typesystem
^^^^^^^^^^
exception-handling="..."
snake-case="yes | no | both"
namespace-begin="..."
- namespace-end="..." >
+ namespace-end="..."
+ doc-package="..."
+ doc-mode = "nested | flat" >
</typesystem>
The **package** attribute is a string describing the package to be used,
For example, for *Qt*, one would specify ``QT_BEGIN_NAMESPACE``,
``QT_END_NAMESPACE``, respectively.
+The *optional* attribute **doc-package** specifies an alternate package name
+for the documentation. This is used for finding the ``qdoc`` or ``doxygen``
+files for the documentation generation. It is mainly relevant for Qt where for
+example the documentation of the ``QtMultimediaWidgets`` module is generated
+into the ``QtMultimedia`` module.
+
+The *optional* attribute **doc-mode** determines whether to nest the
+documentation of inner classes into the outer classes' pages. Its default value
+is **nested**. It is possible to turn this off by specifying **flat** in case
+too many classes are cointained in a namespace, causing excessively long pages.
+However, the referencing may then not fully work.
+
.. _load-typesystem:
load-typesystem
The ``load-typesystem`` node specifies which type systems to load when mapping
multiple libraries to another language or basing one library on another, and
-it is a child of the :ref:`typesystem` node.
+it is a child of the :ref:`typesystem_details` node.
.. code-block:: xml
^^^^^^^^^
The ``rejection`` node rejects the given class, or the specified function
-or field, and it is a child of the :ref:`typesystem` node.
+or field, and it is a child of the :ref:`typesystem_details` node.
.. code-block:: xml
^^^^^^^^^^^^^^
The ``primitive-type`` node describes how a primitive type is mapped from C++ to
-the target language, and is a child of the :ref:`typesystem` node. It may
+the target language, and is a child of the :ref:`typesystem_details` node. It may
contain :ref:`conversion-rule` child nodes. Note that most primitives are
already specified in the QtCore typesystem (see :ref:`primitive-cpp-types`).
^^^^^^^^^^^^^^
The ``namespace-type`` node maps the given C++ namespace to the target
-language, and it is a child of the :ref:`typesystem` node or other
+language, and it is a child of the :ref:`typesystem_details` node or other
``namespace-type`` nodes. It may contain :ref:`add-function`,
:ref:`declare-function`, :ref:`enum-type`, :ref:`extra-includes`,
:ref:`include-element`, :ref:`modify-function`, ``namespace-type``,
since="..."
extends = "..."
files = "..."
+ doc-file = "..."
revision="..." />
</typesystem>
include files whose contents are to be associated with the current module in
case of a namespace spanning several modules.
+The *optional* **doc-file** attribute specifies the base name of a ``.qdoc``
+file where documentation of the class is to be found (``qdoc`` only).
+
.. _enum-type:
enum-type
^^^^^^^^^
The ``enum-type`` node maps the given enum from C++ to the target language,
-and it is a child of the :ref:`typesystem` node. Use
+and it is a child of the :ref:`typesystem_details` node. Use
:ref:`reject-enum-value` child nodes to reject values.
.. code-block:: xml
The ``value-type`` node indicates that the given C++ type is mapped onto the target
language as a value type. This means that it is an object passed by value on C++,
-i.e. it is stored in the function call stack. It is a child of the :ref:`typesystem`
+i.e. it is stored in the function call stack. It is a child of the :ref:`typesystem_details`
node or other type nodes and may contain :ref:`add-function`, :ref:`add-pymethoddef`,
:ref:`configuration-element`, :ref:`declare-function`, :ref:`conversion-rule`,
:ref:`enum-type`, :ref:`extra-includes`, :ref:`include-element`, :ref:`modify-function`,
stream="yes | no"
default-constructor="..."
revision="..."
- snake-case="yes | no | both" />
+ snake-case="yes | no | both"
+ doc-file = "..." />
</typesystem>
The **name** attribute is the fully qualified C++ class name, such as
to override the command line setting for generating bool casts
(see :ref:`bool-cast`).
+The *optional* **doc-file** attribute specifies the base name of a ``.qdoc``
+file where documentation of the class is to be found (``qdoc`` only).
+
.. _object-type:
object-type
The object-type node indicates that the given C++ type is mapped onto the target
language as an object type. This means that it is an object passed by pointer on
-C++ and it is stored on the heap. It is a child of the :ref:`typesystem` node
+C++ and it is stored on the heap. It is a child of the :ref:`typesystem_details` node
or other type nodes and may contain :ref:`add-function`, :ref:`add-pymethoddef`,
:ref:`configuration-element`, :ref:`declare-function`, :ref:`enum-type`,
:ref:`extra-includes`, :ref:`include-element`, :ref:`modify-function`,
qt-register-metatype = "yes | no | base"
stream="yes | no"
revision="..."
- snake-case="yes | no | both" />
+ snake-case="yes | no | both"
+ doc-file = "..." />
</typesystem>
The **name** attribute is the fully qualified C++ class name. If there is no
For the *optional* **polymorphic-id-expression**, **polymorphic-name-function**
and **polymorphic-base** attributes, see :ref:`typediscovery-attributes`.
+The *optional* **doc-file** attribute specifies the base name of a ``.qdoc``
+file where documentation of the class is to be found (``qdoc`` only).
+
interface-type
^^^^^^^^^^^^^^
The ``container-type`` node indicates that the given class is a container and
must be handled using one of the conversion helpers provided by attribute **type**.
-It is a child of the :ref:`typesystem` node and may contain
+It is a child of the :ref:`typesystem_details` node and may contain
:ref:`conversion-rule` child nodes.
.. code-block:: xml
The ``opaque-container`` element can be used to add further instantiations
of :ref:`opaque containers <opaque-containers>` to existing container types
(built-in or specified by :ref:`container-type` in included modules).
-It is a child of the :ref:`typesystem` node.
+It is a child of the :ref:`typesystem_details` node.
.. code-block:: xml
are mostly equivalent to spelling out the typedef in the included header, which
is often complicated when trying to wrap libraries whose source code cannot be
easily extended.
-It is a child of the :ref:`typesystem` node or other type nodes.
+It is a child of the :ref:`typesystem_details` node or other type nodes.
.. code-block:: xml
language type, thus avoiding errors when trying to find a type used in function
signatures and other places. The proper handling of the custom type is meant to
be done by a generator using the APIExractor.
-It is a child of the :ref:`typesystem` node.
+It is a child of the :ref:`typesystem_details` node.
.. code-block:: xml
and requires inserting calls to **getter** to access the pointeee.
Currently, the usage is limited to function return values.
**ref-count-method** specifies the name of the method used to do reference counting.
-It is a child of the :ref:`typesystem` node or other type nodes.
+It is a child of the :ref:`typesystem_details` node or other type nodes.
The *optional* attribute **instantiations** specifies for which instantiations
of the smart pointer wrappers will be generated (comma-separated list).
be followed an equal sign and the Python type name, for example
``instantiations="int=IntPtr,double=DoublePtr"``.
It is also possible to specify a namespace delimited by ``::``.
+By default, the type will be in the namespace of the smart pointer,
+for example, ``std`` for ``std::shared_ptr``. Preceding
+the type name by ``::`` causes it to be in the global namespace.
The *optional* attribute **type** specifies the type:
^^^^^^^^
The ``function`` node indicates that the given C++ global function is mapped
-onto the target language. It is a child of the :ref:`typesystem` node
+onto the target language. It is a child of the :ref:`typesystem_details` node
and may contain a :ref:`modify-function` child node.
.. code-block:: xml
parsed. Normally, include files considered to be system include
files are skipped by the C++ code parser. Its primary use case
is exposing classes from the STL library.
-It is a child of the :ref:`typesystem` node.
+It is a child of the :ref:`typesystem_details` node.
.. code-block:: xml
The ``template`` node registers a template that can be used to avoid
duplicate code when extending the generated code, and it is a child of the
-:ref:`typesystem` node.
+:ref:`typesystem_details` node.
.. code-block:: xml
.. code-block:: c++
- long a = PyLong_AS_LONG(PyTuple_GET_ITEM(%PYTHON_ARGUMENTS, 0));
+ long a = PyLong_AS_LONG(PyTuple_GetItem(%PYTHON_ARGUMENTS, 0));
The generator tries to be smart with attributions, but it will work for the
.. code-block:: c++
- Py_DECREF(PyTuple_GET_ITEM(%PYTHON_ARGUMENTS, 0));
- PyTuple_SET_ITEM(%PYTHON_ARGUMENTS, 0, PyLong_FromLong(10));
+ Py_DECREF(PyTuple_GetItem(%PYTHON_ARGUMENTS, 0));
+ PyTuple_SetItem(%PYTHON_ARGUMENTS, 0, PyLong_FromLong(10));
.. _pyself:
static constexpr auto ENABLE_PYSIDE_EXTENSIONS = "enable-pyside-extensions"_L1;
static constexpr auto AVOID_PROTECTED_HACK = "avoid-protected-hack"_L1;
+static constexpr auto DISABLED_OPTIMIZATIONS = "unoptimize"_L1;
struct GeneratorOptions
{
bool usePySideExtensions = false;
bool avoidProtectedHack = false;
+ Generator::CodeOptimization optimizations = Generator::AllCodeOptimizations;
};
struct Generator::GeneratorPrivate
u"Avoid the use of the '#define protected public' hack."_s},
{ENABLE_PYSIDE_EXTENSIONS,
u"Enable PySide extensions, such as support for signal/slots,\n"
- "use this if you are creating a binding for a Qt-based library."_s}
+ "use this if you are creating a binding for a Qt-based library."_s},
+ {DISABLED_OPTIMIZATIONS,
+ "Disable optimization options"_L1}
};
}
explicit GeneratorOptionsParser(GeneratorOptions *o) : m_options(o) {}
bool handleBoolOption(const QString &key, OptionSource source) override;
+ bool handleOption(const QString &key, const QString &value, OptionSource source) override;
private:
GeneratorOptions *m_options;
return false;
}
+bool GeneratorOptionsParser::handleOption(const QString & key, const QString & value,
+ OptionSource source)
+{
+ if (source == OptionSource::CommandLineSingleDash)
+ return false;
+
+ if (key == DISABLED_OPTIMIZATIONS) {
+ const auto tokens = QStringView{value}.split(u',');
+ for (const auto &tok : tokens) {
+ if (tok == "fullname"_L1)
+ m_options->optimizations.setFlag(Generator::RemoveFullnameField, false);
+ else if (tok == "compression"_L1)
+ m_options->optimizations.setFlag(Generator::CompressSignatureStrings, false);
+ else if (tok == "folding"_L1)
+ m_options->optimizations.setFlag(Generator::FoldCommonTailCode, false);
+ else if (tok == "all"_L1)
+ m_options->optimizations = Generator::CodeOptimization(0);
+ else
+ return false;
+ }
+ return true;
+ }
+
+ return false;
+}
+
std::shared_ptr<OptionsParser> Generator::createOptionsParser()
{
return std::make_shared<GeneratorOptionsParser>(&GeneratorPrivate::m_options);
m_d->outDir = outDir;
}
-bool Generator::generateFileForContext(const GeneratorContext &context)
+QString Generator::directoryForContext(const GeneratorContext &context) const
{
- const auto cls = context.metaClass();
- auto typeEntry = cls->typeEntry();
-
- if (!shouldGenerate(typeEntry))
- return true;
-
- const QString fileName = fileNameForContext(context);
- if (fileName.isEmpty())
- return true;
-
- QString filePath = outputDirectory() + u'/'
- + subDirectoryForPackage(typeEntry->targetLangPackage())
- + u'/' + fileName;
- FileOut fileOut(filePath);
-
- generateClass(fileOut.stream, context);
+ return m_d->outDir + u'/'
+ + subDirectoryForPackage(context.metaClass()->typeEntry()->targetLangPackage());
+}
- fileOut.done();
- return true;
+void Generator::generateSmartPointerClass(TextStream &,
+ const QString &,
+ const GeneratorContext &)
+{
}
QString Generator::getFileNameBaseForSmartPointer(const AbstractMetaType &smartPointerType)
GeneratorContext Generator::contextForClass(const AbstractMetaClassCPtr &c) const
{
GeneratorContext result;
+ result.m_type = GeneratorContext::Class;
result.m_metaClass = c;
return result;
}
bool Generator::generate()
{
+ QList<GeneratorContext> contexts;
+ contexts.reserve(m_d->api.classes().size());
+
for (const auto &cls : m_d->api.classes()) {
- if (!generateFileForContext(contextForClass(cls)))
- return false;
auto te = cls->typeEntry();
- if (shouldGenerate(te) && te->isPrivate())
- m_d->m_hasPrivateClasses = true;
+ if (shouldGenerate(te)) {
+ contexts.append(contextForClass(cls));
+ if (te->isPrivate())
+ m_d->m_hasPrivateClasses = true;
+ }
+ }
+
+ while (!contexts.isEmpty()) {
+ const auto context = contexts.takeFirst();
+ const QString targetDirectory = directoryForContext(context);
+ FileOut fileOut(targetDirectory + u'/' + fileNameForContext(context));
+ generateClass(fileOut.stream, targetDirectory, context, &contexts);
+ fileOut.done();
}
+ // Generate smart pointers.
for (const auto &smp: m_d->api.instantiatedSmartPointers()) {
- AbstractMetaClassCPtr pointeeClass;
- const auto instantiatedType = smp.type.instantiations().constFirst().typeEntry();
- if (instantiatedType->isComplex()) // not a C++ primitive
- pointeeClass = AbstractMetaClass::findClass(m_d->api.classes(), instantiatedType);
- if (!generateFileForContext(contextForSmartPointer(smp.specialized, smp.type,
- pointeeClass))) {
- return false;
+ if (shouldGenerate(smp.specialized->typeEntry())) {
+ const auto context = contextForSmartPointer(smp.specialized, smp.type, smp.pointee);
+ const QString targetDirectory = directoryForContext(context);
+ FileOut fileOut(targetDirectory + u'/' + fileNameForContext(context));
+ generateSmartPointerClass(fileOut.stream, targetDirectory, context);
+ fileOut.done();
}
}
return finishGeneration();
return GeneratorPrivate::m_options.avoidProtectedHack;
}
+Generator::CodeOptimization Generator::optimizations()
+{
+ return GeneratorPrivate::m_options.optimizations;
+}
+
QString Generator::getFullTypeName(TypeEntryCPtr type)
{
QString result = type->qualifiedCppName();
* you must subclass this to create your own generators.
*/
class Generator
-{;
+{
public:
Q_DISABLE_COPY_MOVE(Generator)
};
Q_DECLARE_FLAGS(FileNameFlags, FileNameFlag)
+ enum CodeOptimizationFlag {
+ RemoveFullnameField = 0x00000001,
+ CompressSignatureStrings = 0x00000002,
+ FoldCommonTailCode = 0x00000004,
+
+ AllCodeOptimizations = 0x000000ff
+ };
+ Q_DECLARE_FLAGS(CodeOptimization, CodeOptimizationFlag)
+
Generator();
virtual ~Generator();
/// Returns true if the generated code should not use the
/// "#define protected public" hack.
static bool avoidProtectedHack();
+ /// Returns optimization flags.
+ static CodeOptimization optimizations();
/**
* Retrieves the name of the currently processed module.
contextForSmartPointer(const AbstractMetaClassCPtr &c, const AbstractMetaType &t,
const AbstractMetaClassCPtr &pointeeClass = {});
- /// Generates a file for given AbstractMetaClass or AbstractMetaType (smart pointer case).
- bool generateFileForContext(const GeneratorContext &context);
-
/// Returns the file base name for a smart pointer.
static QString getFileNameBaseForSmartPointer(const AbstractMetaType &smartPointerType);
virtual bool doSetup() = 0;
/**
- * Write the bindding code for an AbstractMetaClass.
+ * Write the binding code for an AbstractMetaClass.
* This is called by generate method.
* \param s text stream to write the generated output
* \param metaClass the class that should be generated
*/
- virtual void generateClass(TextStream &s, const GeneratorContext &classContext) = 0;
+ virtual void generateClass(TextStream &s,
+ const QString &targetDir,
+ const GeneratorContext &classContext,
+ QList<GeneratorContext> *contexts) = 0;
+ virtual void generateSmartPointerClass(TextStream &s,
+ const QString &targetDir,
+ const GeneratorContext &classContext);
virtual bool finishGeneration() = 0;
/**
static QString m_gsp;
private:
+ QString directoryForContext(const GeneratorContext &context) const;
+
struct GeneratorPrivate;
GeneratorPrivate *m_d;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Generator::Options)
Q_DECLARE_OPERATORS_FOR_FLAGS(Generator::FileNameFlags)
+Q_DECLARE_OPERATORS_FOR_FLAGS(Generator::CodeOptimization)
using GeneratorPtr = std::shared_ptr<Generator>;
using Generators = QList<GeneratorPtr>;
QString GeneratorContext::effectiveClassName() const
{
+ Q_ASSERT(hasClass());
if (m_type == SmartPointer)
return m_preciseClassType.cppSignature();
return m_type == WrappedClass ? m_wrappername : m_metaClass->qualifiedCppName();
friend class ShibokenGenerator;
friend class Generator;
public:
- enum Type { Class, WrappedClass, SmartPointer };
+ enum Type { Class, WrappedClass, SmartPointer,
+ GlobalFunction // No class contained
+ };
GeneratorContext() = default;
- AbstractMetaClassCPtr metaClass() const { return m_metaClass; }
- const AbstractMetaType &preciseType() const { return m_preciseClassType; }
- AbstractMetaClassCPtr pointeeClass() const { return m_pointeeClass; }
+ const AbstractMetaClassCPtr &metaClass() const
+ {
+ Q_ASSERT(hasClass());
+ return m_metaClass;
+ }
+
+ const AbstractMetaType &preciseType() const
+ {
+ Q_ASSERT(forSmartPointer());
+ return m_preciseClassType;
+ }
+
+ AbstractMetaClassCPtr pointeeClass() const
+ {
+
+ Q_ASSERT(forSmartPointer());
+ return m_pointeeClass;
+ }
bool forSmartPointer() const { return m_type == SmartPointer; }
bool useWrapper() const { return m_type == WrappedClass; }
+ bool hasClass() const { return m_type != GlobalFunction; }
QString wrapperName() const;
/// Returns the wrapper name in case of useWrapper(), the qualified class
AbstractMetaClassCPtr m_pointeeClass;
AbstractMetaType m_preciseClassType;
QString m_wrappername;
- Type m_type = Class;
+ Type m_type = GlobalFunction;
};
QDebug operator<<(QDebug debug, const GeneratorContext &c);
static inline void printVerAndBanner()
{
- std::cout << appName << " v" << SHIBOKEN_VERSION << std::endl;
- std::cout << "Copyright (C) 2016 The Qt Company Ltd." << std::endl;
+ std::cout << appName << " v" << SHIBOKEN_VERSION
+ << "\nCopyright (C) 2016 The Qt Company Ltd.\n";
}
static inline void errorPrint(const QString &s, const QStringList &arguments)
}
if (commonOptions.typeSystemFileName.isEmpty()) {
- std::cout << "You must specify a Type System file." << std::endl << helpHint;
+ std::cout << "You must specify a Type System file.\n" << helpHint;
return EXIT_FAILURE;
}
TypeDatabase::instance()->logUnmatched();
const QByteArray doneMessage = ReportHandler::doneMessage();
- std::cout << doneMessage.constData() << std::endl;
+ std::cout << doneMessage.constData() << '\n';
return EXIT_SUCCESS;
}
try {
ex = shibokenMain(argV);
} catch (const std::exception &e) {
- std::cerr << appName << " error: " << e.what() << std::endl;
+ std::cerr << appName << " error: " << e.what() << '\n';
ex = EXIT_FAILURE;
}
return ex;
#include <complextypeentry.h>
#include <flagstypeentry.h>
#include <primitivetypeentry.h>
+#include <typesystemtypeentry.h>
#include <qtdocparser.h>
#include <doxygenparser.h>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
+#include <QtCore/QOperatingSystemVersion>
#include <QtCore/QSet>
#include <algorithm>
#include <limits>
+#include <unordered_set>
using namespace Qt::StringLiterals;
return metaClass->fullName();
}
+struct DocClassEntry
+{
+ QString name;
+ QString fullName;
+ QString file;
+};
+
+static bool classEntryLessThan(const DocClassEntry &e1, const DocClassEntry &e2)
+{
+ return e1.name < e2.name;
+}
+
struct DocPackage
{
- QStringList classPages;
+ QList<DocClassEntry> classPages;
QStringList decoratorPages;
AbstractMetaFunctionCList globalFunctions;
AbstractMetaEnumList globalEnums;
return lhs.name < rhs.name;
}
+struct ResolvedDocImage
+{
+ QString name;
+ QString absoluteSourceFilePath;
+ QString targetDir;
+};
+
+QDebug operator<<(QDebug debug, const ResolvedDocImage &i)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "ResolvedDocImage(\"" << i.name << "\", \"" << i.absoluteSourceFilePath
+ << "\" -> \"" << i.targetDir << "\")";
+ return debug;
+}
+
static QString propertyRefTarget(const QString &name)
{
QString result = name;
DocPackage *docPackage, QStringList *extraTocEntries)
{
// Strip to "Property.rst" in output directory
- const QString newFileName = fi.fileName().mid(moduleName.size() + 1);
+ const QString newFileName = fi.fileName().sliced(moduleName.size() + 1);
+ const QString fullClassName = fi.completeBaseName().sliced(moduleName.size() + 1);
QFile sourceFile(fi.absoluteFilePath());
if (!sourceFile.open(QIODevice::ReadOnly|QIODevice::Text)) {
qCWarning(lcShibokenDoc, "%s", qPrintable(msgCannotOpenForReading(sourceFile)));
return;
}
targetFile.write(contents);
- if (contents.contains("decorator::"))
+ if (contents.contains("decorator::")) {
docPackage->decoratorPages.append(newFileName);
- else
- docPackage->classPages.append(newFileName);
+ } else {
+ QString name = fullClassName;
+ auto dot = name.lastIndexOf(u'.');
+ if (dot != -1)
+ name.remove(0, dot + 1);
+ docPackage->classPages.append({name, fullClassName, newFileName});
+ }
extraTocEntries->append(fileNameToTocEntry(newFileName));
}
}
void QtDocGenerator::writeFormattedBriefText(TextStream &s, const Documentation &doc,
- const QString &scope) const
+ const QString &scope,
+ QtXmlToSphinxImages *images) const
{
- writeFormattedText(s, doc.brief(), doc.format(), scope);
+ writeFormattedText(s, doc.brief(), doc.format(), scope, images);
}
void QtDocGenerator::writeFormattedDetailedText(TextStream &s, const Documentation &doc,
- const QString &scope) const
+ const QString &scope,
+ QtXmlToSphinxImages *images) const
{
- writeFormattedText(s, doc.detailed(), doc.format(), scope);
+ writeFormattedText(s, doc.detailed(), doc.format(), scope, images);
}
void QtDocGenerator::writeFormattedText(TextStream &s, const QString &doc,
- Documentation::Format format,
- const QString &scope) const
+ DocumentationFormat format,
+ const QString &scope,
+ QtXmlToSphinxImages *images) const
{
- if (format == Documentation::Native) {
+ if (format == DocumentationFormat::Native) {
QtXmlToSphinx x(this, m_options.parameters, doc, scope);
s << x;
+ images->append(x.images());
} else {
const auto lines = QStringView{doc}.split(u'\n');
int typesystemIndentation = std::numeric_limits<int>::max();
writeInheritanceList(s, res, "Inherits from");
}
-void QtDocGenerator::generateClass(TextStream &s, const GeneratorContext &classContext)
+void QtDocGenerator::generateClass(TextStream &s, const QString &targetDir,
+ const GeneratorContext &classContext,
+ QList<GeneratorContext> *contexts)
{
- AbstractMetaClassCPtr metaClass = classContext.metaClass();
- qCDebug(lcShibokenDoc).noquote().nospace() << "Generating Documentation for " << metaClass->fullName();
+ // PYSIDE-2689: Write a (dummy) headline to fix the search contexts.
+ s << headline(classContext.metaClass()->fullName(), '#');
+ generateClassRecursion(s, targetDir, classContext, contexts);
+}
- m_packages[metaClass->package()].classPages << fileNameForContext(classContext);
+static inline TypeSystem::DocMode classDocMode(const AbstractMetaClassCPtr &metaClass)
+{
+ return typeSystemTypeEntry(metaClass->typeEntry())->docMode();
+}
- m_docParser->setPackageName(metaClass->package());
- m_docParser->fillDocumentation(std::const_pointer_cast<AbstractMetaClass>(metaClass));
+void QtDocGenerator::generateClassRecursion(TextStream &s, const QString &targetDir,
+ const GeneratorContext &classContext,
+ QList<GeneratorContext> *contexts)
+{
+ const AbstractMetaClassCPtr &metaClass = classContext.metaClass();
+
+ qCDebug(lcShibokenDoc, "Generating Documentation for %s", qPrintable(metaClass->fullName()));
+
+ m_packages[metaClass->package()].classPages.append({metaClass->name(), metaClass->fullName(),
+ fileNameForContext(classContext)});
+
+ doGenerateClass(s, targetDir, metaClass);
+
+ if (classDocMode(metaClass) == TypeSystem::DocMode::Nested) {
+ QList<GeneratorContext> innerContexts;
+ for (qsizetype i = 0; i < contexts->size(); ) {
+ if (contexts->at(i).metaClass()->targetLangEnclosingClass() == metaClass)
+ innerContexts.append(contexts->takeAt(i));
+ else
+ ++i;
+ }
+ if (!innerContexts.isEmpty()) {
+ s << indent;
+ for (const auto &innerClassContext : std::as_const(innerContexts))
+ generateClassRecursion(s, targetDir, innerClassContext, contexts);
+ s << outdent;
+ }
+ }
+}
+
+void QtDocGenerator::doGenerateClass(TextStream &s, const QString &targetDir,
+ const AbstractMetaClassCPtr &metaClass)
+{
+ qCDebug(lcShibokenDoc).noquote().nospace() << "Generating Documentation for " << metaClass->fullName();
+
+ const QString sourceFile =
+ m_docParser->fillDocumentation(std::const_pointer_cast<AbstractMetaClass>(metaClass));
s << currentModule(metaClass->package()) << pyClass(metaClass->name());
Indentation indent(s);
+ QtXmlToSphinxImages parsedImages;
auto documentation = metaClass->documentation();
const QString scope = classScope(metaClass);
if (documentation.hasBrief())
- writeFormattedBriefText(s, documentation, scope);
+ writeFormattedBriefText(s, documentation, scope, &parsedImages);
if (!metaClass->baseClasses().isEmpty()) {
if (m_options.inheritanceDiagram) {
s << '\n' << headline("Detailed Description") << ".. _More:\n";
- writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, metaClass);
- if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, metaClass))
- writeFormattedDetailedText(s, documentation, scope);
- writeInjectDocumentation(s, TypeSystem::DocModificationAppend, metaClass);
+ writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, metaClass,
+ &parsedImages);
+ if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, metaClass, &parsedImages))
+ writeFormattedDetailedText(s, documentation, scope, &parsedImages);
+ writeInjectDocumentation(s, TypeSystem::DocModificationAppend, metaClass, &parsedImages);
- writeEnums(s, metaClass->enums(), scope);
+ writeEnums(s, metaClass->enums(), scope, &parsedImages);
if (!doc.properties.isEmpty())
- writeProperties(s, doc, metaClass);
+ writeProperties(s, doc, metaClass, &parsedImages);
if (!metaClass->isNamespace())
- writeFields(s, metaClass);
+ writeFields(s, metaClass, &parsedImages);
- writeFunctions(s, doc.allFunctions, metaClass, scope);
+ writeFunctions(s, doc.allFunctions, metaClass, scope, &parsedImages);
+
+ copyParsedImages(parsedImages, {sourceFile}, targetDir);
}
void QtDocGenerator::writeFunctionToc(TextStream &s, const QString &title,
void QtDocGenerator::writeProperties(TextStream &s,
const GeneratorDocumentation &doc,
- const AbstractMetaClassCPtr &cppClass) const
+ const AbstractMetaClassCPtr &cppClass,
+ QtXmlToSphinxImages *images) const
{
s << "\n.. note:: Properties can be used directly when "
<< "``from __feature__ import true_property`` is used or via accessor "
const QString type = translateToPythonType(prop.type, cppClass, /* createRef */ false);
s << ".. py:property:: " << propertyRefTarget(prop.name)
<< "\n :type: " << type << "\n\n\n";
- if (!prop.documentation.isEmpty())
- writeFormattedText(s, prop.documentation.detailed(), Documentation::Native, scope);
+ if (!prop.documentation.isEmpty()) {
+ writeFormattedText(s, prop.documentation.detailed(), DocumentationFormat::Native,
+ scope, images);
+ }
s << "**Access functions:**\n";
if (prop.getter)
s << " * " << functionRef(prop.getter->name()) << '\n';
}
void QtDocGenerator::writeEnums(TextStream &s, const AbstractMetaEnumList &enums,
- const QString &scope) const
+ const QString &scope, QtXmlToSphinxImages *images) const
{
for (const AbstractMetaEnum &en : enums) {
s << pyClass(en.name());
Indentation indent(s);
- writeFormattedDetailedText(s, en.documentation(), scope);
+ writeFormattedDetailedText(s, en.documentation(), scope, images);
const auto version = versionOf(en.typeEntry());
if (!version.isNull())
s << rstVersionAdded(version);
}
-void QtDocGenerator::writeFields(TextStream &s, const AbstractMetaClassCPtr &cppClass) const
+void QtDocGenerator::writeFields(TextStream &s, const AbstractMetaClassCPtr &cppClass,
+ QtXmlToSphinxImages *images) const
{
constexpr auto section_title = ".. attribute:: "_L1;
const QString scope = classScope(cppClass);
for (const AbstractMetaField &field : cppClass->fields()) {
s << section_title << cppClass->fullName() << "." << field.name() << "\n\n";
- writeFormattedDetailedText(s, field.documentation(), scope);
+ writeFormattedDetailedText(s, field.documentation(), scope, images);
}
}
}
}
+void QtDocGenerator::writeFormattedText(TextStream &s, const DocModification &mod,
+ const QString &scope, QtXmlToSphinxImages *images) const
+{
+ const bool note = mod.emphasis() == DocumentationEmphasis::LanguageNote;
+ if (note)
+ s << ".. admonition:: Python Language Note\n\n" << indent;
+ writeFormattedText(s, mod.code(), mod.format(), scope, images);
+ if (note)
+ s << outdent;
+}
+
bool QtDocGenerator::writeDocModifications(TextStream &s,
const DocModificationList &mods,
TypeSystem::DocModificationMode mode,
- const QString &scope) const
+ const QString &scope,
+ QtXmlToSphinxImages *images) const
{
bool didSomething = false;
for (const DocModification &mod : mods) {
if (mod.mode() == mode) {
- switch (mod.format()) {
- case TypeSystem::NativeCode:
- writeFormattedText(s, mod.code(), Documentation::Native, scope);
- didSomething = true;
- break;
- case TypeSystem::TargetLangCode:
- writeFormattedText(s, mod.code(), Documentation::Target, scope);
- didSomething = true;
- break;
- default:
- break;
- }
+ writeFormattedText(s, mod, scope, images);
+ didSomething = true;
}
}
return didSomething;
bool QtDocGenerator::writeInjectDocumentation(TextStream &s,
TypeSystem::DocModificationMode mode,
- const AbstractMetaClassCPtr &cppClass) const
+ const AbstractMetaClassCPtr &cppClass,
+ QtXmlToSphinxImages *images) const
{
const bool didSomething =
writeDocModifications(s, DocParser::getDocModifications(cppClass),
- mode, classScope(cppClass));
+ mode, classScope(cppClass), images);
s << '\n';
// FIXME PYSIDE-7: Deprecate the use of doc string on glue code.
TypeSystem::DocModificationMode mode,
const DocModificationList &modifications,
const AbstractMetaFunctionCPtr &func,
- const QString &scope) const
+ const QString &scope,
+ QtXmlToSphinxImages *images) const
{
- const bool didSomething = writeDocModifications(s, modifications, mode, scope);
+ const bool didSomething = writeDocModifications(s, modifications, mode, scope, images);
s << '\n';
// FIXME PYSIDE-7: Deprecate the use of doc string on glue code.
if (createRef)
enumName.prepend(enumTypeEntry->targetLangPackage() + u'.');
return "Combination of "_L1 + (createRef ? toRef(enumName) : enumName);
- } else if (type.isEnum()) {
+ }
+ if (type.isEnum()) {
auto enumTypeEntry = std::static_pointer_cast<const EnumTypeEntry>(type.typeEntry());
auto enumName = enumTypeEntry->targetLangName();
if (createRef)
}
void QtDocGenerator::writeFunctions(TextStream &s, const AbstractMetaFunctionCList &funcs,
- const AbstractMetaClassCPtr &cppClass, const QString &scope)
+ const AbstractMetaClassCPtr &cppClass, const QString &scope,
+ QtXmlToSphinxImages *images) const
{
QString lastName;
for (const auto &func : funcs) {
const bool indexed = func->name() != lastName;
lastName = func->name();
- writeFunction(s, func, cppClass, scope, indexed);
+ writeFunction(s, func, images, cppClass, scope, indexed);
}
}
void QtDocGenerator::writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ QtXmlToSphinxImages *images,
const AbstractMetaClassCPtr &cppClass,
- const QString &scope, bool indexed)
+ const QString &scope, bool indexed) const
{
const auto modifications = DocParser::getDocModifications(func, cppClass);
s << rstDeprecationNote("function");
}
- writeFunctionDocumentation(s, func, modifications, scope);
+ writeFunctionDocumentation(s, func, modifications, scope, images);
if (auto propIndex = func->propertySpecIndex(); propIndex >= 0) {
const QString name = cppClass->propertySpecs().at(propIndex).name();
void QtDocGenerator::writeFunctionDocumentation(TextStream &s, const AbstractMetaFunctionCPtr &func,
const DocModificationList &modifications,
- const QString &scope) const
+ const QString &scope,
+ QtXmlToSphinxImages *images) const
{
- writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, modifications, func, scope);
- if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, modifications, func, scope)) {
- writeFormattedBriefText(s, func->documentation(), scope);
- writeFormattedDetailedText(s, func->documentation(), scope);
+ writeInjectDocumentation(s, TypeSystem::DocModificationPrepend, modifications,
+ func, scope, images);
+ if (!writeInjectDocumentation(s, TypeSystem::DocModificationReplace, modifications,
+ func, scope, images)) {
+ writeFormattedBriefText(s, func->documentation(), scope, images);
+ writeFormattedDetailedText(s, func->documentation(), scope, images);
}
- writeInjectDocumentation(s, TypeSystem::DocModificationAppend, modifications, func, scope);
+ writeInjectDocumentation(s, TypeSystem::DocModificationAppend, modifications,
+ func, scope, images);
}
-static QStringList fileListToToc(const QStringList &items)
-{
- QStringList result;
- result.reserve(items.size());
- std::transform(items.cbegin(), items.cend(), std::back_inserter(result),
- fileNameToTocEntry);
- return result;
-}
-
-static QStringList functionListToToc(const AbstractMetaFunctionCList &functions)
-{
- QStringList result;
- result.reserve(functions.size());
- for (const auto &f : functions)
- result.append(f->name());
- // Functions are sorted by the Metabuilder; erase overloads
- result.erase(std::unique(result.begin(), result.end()), result.end());
- return result;
-}
-
-static QStringList enumListToToc(const AbstractMetaEnumList &enums)
-{
- QStringList result;
- result.reserve(enums.size());
- for (const auto &e : enums)
- result.append(e.name());
- return result;
-}
+using TocMap = QMap<QChar, QStringList>;
// Sort entries for a TOC by first character, dropping the
// leading common Qt prefixes like 'Q'.
return idx < size ? key.at(idx).toUpper() : u'A';
}
+static TocMap classEntryListToToc(const QList<DocClassEntry> &entries,
+ TypeSystem::DocMode docMode)
+{
+ const bool fullyQualified = docMode == TypeSystem::DocMode::Nested;
+ TocMap result;
+ // Sort by name, use full href
+ for (const auto &e : entries)
+ result[sortKey(e.name)] << (fullyQualified ? e.fullName : e.name);
+ return result;
+}
+
+static TocMap fileListToToc(const QStringList &items)
+{
+ TocMap result;
+ for (const auto &item : items) {
+ const QString entry = fileNameToTocEntry(item);
+ result[sortKey(entry)] << entry;
+ }
+ return result;
+}
+
+static TocMap functionListToToc(const AbstractMetaFunctionCList &functions)
+{
+ TocMap result;
+ // Functions are sorted by the Metabuilder; erase overloads
+ std::unordered_set<QString> seenNames;
+ for (const auto &f : functions) {
+ const QString &name = f->name();
+ if (seenNames.find(name) == seenNames.end()) {
+ seenNames.insert(name);
+ result[sortKey(name)] << name;
+ }
+ }
+ return result;
+}
+
+static TocMap enumListToToc(const AbstractMetaEnumList &enums)
+{
+ TocMap result;
+ for (const auto &e : enums)
+ result[sortKey(e.name())] << e.name();
+ return result;
+}
+
static void writeFancyToc(TextStream& s, QAnyStringView title,
- const QStringList& items,
+ const TocMap &tocMap,
QLatin1StringView referenceType)
{
- using TocMap = QMap<QChar, QStringList>;
-
- if (items.isEmpty())
+ if (tocMap.isEmpty())
return;
- TocMap tocMap;
- for (const QString &item : items)
- tocMap[sortKey(item)] << item;
-
- static const qsizetype numColumns = 4;
+ qsizetype maxColumnCount = 0;
+ for (auto it = tocMap.cbegin(), end = tocMap.cend(); it != end; ++it) {
+ if (it.value().size() > maxColumnCount)
+ maxColumnCount = it.value().size();
+ }
+ const auto columnCount = 1 + std::min(maxColumnCount, qsizetype(3)); // With header
QtXmlToSphinx::Table table;
for (auto it = tocMap.cbegin(), end = tocMap.cend(); it != end; ++it) {
const QString charEntry = u"**"_s + it.key() + u"**"_s;
row << QtXmlToSphinx::TableCell(charEntry);
for (const QString &item : std::as_const(it.value())) {
- if (row.size() >= numColumns) {
+ if (row.size() >= columnCount) {
table.appendRow(row);
row.clear();
row << QtXmlToSphinx::TableCell(QString{});
}
- const QString entry = "* :"_L1 + referenceType + ":`"_L1 + item + u'`';
+ QString entry = "* :"_L1 + referenceType + ":`"_L1;
+ if (item.contains(u'.'))
+ entry += u'~';
+ entry += item + u'`';
row << QtXmlToSphinx::TableCell(entry);
}
- if (row.size() > 1)
+ if (row.size() > 1) {
+ if (const auto padColSpan = columnCount - row.size(); padColSpan > 0) {
+ QtXmlToSphinx::TableCell padding(QString{});
+ padding.colSpan = int(padColSpan);
+ row.append(padding);
+ }
table.appendRow(row);
+ }
}
table.normalize();
{
QFile inheritanceFile(m_options.inheritanceFile);
if (!inheritanceFile.open(QIODevice::WriteOnly | QIODevice::Text))
- throw Exception(msgCannotOpenForWriting(m_options.inheritanceFile));
+ throw Exception(msgCannotOpenForWriting(inheritanceFile));
QJsonObject dict;
for (const auto &c : api().classes()) {
functions->end());
}
+// Extract images from RstDocs
+static bool imagesFromRstDocs(const QByteArray &rstDoc, const QString &scope,
+ QtXmlToSphinxImages *images)
+{
+ bool result = false;
+ static const QByteArray imageTag = ".. image:: "_ba;
+
+ for (qsizetype pos = 0; pos < rstDoc.size(); ) {
+ pos = rstDoc.indexOf(imageTag, pos);
+ if (pos == -1)
+ break;
+ pos += imageTag.size();
+ const auto newLinePos = rstDoc.indexOf('\n', pos);
+ if (newLinePos == -1)
+ break;
+ const auto image = rstDoc.sliced(pos, newLinePos - pos).trimmed();
+ images->append({scope, QString::fromUtf8(image)});
+ result = true;
+ pos = newLinePos + 1;
+ }
+ return result;
+}
+
void QtDocGenerator::writeModuleDocumentation()
{
+ auto *typeDb = TypeDatabase::instance();
for (auto it = m_packages.begin(), end = m_packages.end(); it != end; ++it) {
auto &docPackage = it.value();
- std::sort(docPackage.classPages.begin(), docPackage.classPages.end());
+ std::sort(docPackage.classPages.begin(), docPackage.classPages.end(), classEntryLessThan);
QString key = it.key();
key.replace(u'.', u'/');
QString outputDir = outputDirectory() + u'/' + key;
FileOut output(outputDir + u"/index.rst"_s);
+ QtXmlToSphinxImages parsedImages;
TextStream& s = output.stream;
const QString &title = it.key();
// information when neeeded. For example, the RST files in the extras directory
// doesn't include the PySide# prefix in their names.
QString moduleName = it.key();
- const int lastIndex = moduleName.lastIndexOf(u'.');
+ const auto lastIndex = moduleName.lastIndexOf(u'.');
if (lastIndex >= 0)
moduleName.remove(0, lastIndex + 1);
<< ":maxdepth: 1\n\n";
if (hasGlobals)
s << globalsPage << '\n';
- for (const QString &className : std::as_const(docPackage.classPages))
- s << className << '\n';
+ for (const auto &e : std::as_const(docPackage.classPages))
+ s << e.file << '\n';
s << "\n\n" << outdent << outdent << headline("Detailed Description");
// module doc is always wrong and C++istic, so go straight to the extra directory!
- QFile moduleDoc(m_options.extraSectionDir + u'/' + moduleName
- + u".rst"_s);
- if (moduleDoc.open(QIODevice::ReadOnly | QIODevice::Text)) {
- s << moduleDoc.readAll();
+ const QString moduleDocRstFileName = m_options.extraSectionDir + u'/' + moduleName + u".rst"_s;
+ QStringList sourceFileNames;
+
+ QString context = it.key();
+ // Get the WebXML source file for image resolution if we
+ // are re-using images from it in our .rst.
+ QtXmlToSphinx::stripPythonQualifiers(&context);
+ const ModuleDocumentation moduleDocumentation =
+ m_docParser->retrieveModuleDocumentation(it.key());
+ const Documentation &webXmlModuleDoc = moduleDocumentation.documentation;
+ if (webXmlModuleDoc.hasSourceFile())
+ sourceFileNames.append(webXmlModuleDoc.sourceFile());
+ if (QFileInfo::exists(moduleDocRstFileName)) {
+ QFile moduleDoc(moduleDocRstFileName);
+ if (!moduleDoc.open(QIODevice::ReadOnly | QIODevice::Text))
+ throw Exception(msgCannotOpenForReading(moduleDoc));
+ const QByteArray rstDoc = moduleDoc.readAll();
+ s << rstDoc;
moduleDoc.close();
- } else {
+ if (imagesFromRstDocs(rstDoc, context, &parsedImages))
+ sourceFileNames.append(moduleDocRstFileName);
+ } else if (!webXmlModuleDoc.isEmpty()) {
// try the normal way
- Documentation moduleDoc = m_docParser->retrieveModuleDocumentation(it.key());
- if (moduleDoc.format() == Documentation::Native) {
- QString context = it.key();
- QtXmlToSphinx::stripPythonQualifiers(&context);
- QtXmlToSphinx x(this, m_options.parameters, moduleDoc.detailed(), context);
+ if (webXmlModuleDoc.format() == DocumentationFormat::Native) {
+ QtXmlToSphinx x(this, m_options.parameters, webXmlModuleDoc.detailed(), context);
s << x;
+ parsedImages += x.images();
} else {
- s << moduleDoc.detailed();
+ s << webXmlModuleDoc.detailed();
}
}
- writeFancyToc(s, "List of Classes", fileListToToc(docPackage.classPages),
+ if (!moduleDocumentation.qmlTypesUrl.isEmpty()) {
+ s << '\n' << headline("List of QML types")
+ << "\n * `" << moduleName<< " QML Types <"
+ << moduleDocumentation.qmlTypesUrl << ">`_\n\n";
+ }
+
+ TypeSystemTypeEntryCPtr typeSystemEntry = typeDb->findTypeSystemType(it.key());
+ Q_ASSERT(typeSystemEntry);
+ writeFancyToc(s, "List of Classes", classEntryListToToc(docPackage.classPages,
+ typeSystemEntry->docMode()),
"class"_L1);
writeFancyToc(s, "List of Decorators", fileListToToc(docPackage.decoratorPages),
"deco"_L1);
"any"_L1);
output.done();
+ copyParsedImages(parsedImages, sourceFileNames, outputDir);
if (hasGlobals)
writeGlobals(it.key(), outputDir + u'/' + globalsPage, docPackage);
const DocPackage &docPackage)
{
FileOut output(fileName);
+ QStringList docFiles;
+ QtXmlToSphinxImages parsedImages;
TextStream &s = output.stream;
// Write out functions with injected documentation
if (!docPackage.globalFunctions.isEmpty()) {
+ for (const auto &func : docPackage.globalFunctions) {
+ const Documentation &doc = func->documentation();
+ if (doc.hasSourceFile() && !docFiles.contains(doc.sourceFile()))
+ docFiles.append(doc.sourceFile());
+ }
s << currentModule(package) << headline("Functions");
- writeFunctions(s, docPackage.globalFunctions, {}, {});
+ writeFunctions(s, docPackage.globalFunctions, {}, {}, &parsedImages);
}
if (!docPackage.globalEnums.isEmpty()) {
s << headline("Enumerations");
- writeEnums(s, docPackage.globalEnums, package);
+ for (const auto &globalEnum : docPackage.globalEnums) {
+ const Documentation &doc = globalEnum.documentation();
+ if (doc.hasSourceFile() && !docFiles.contains(doc.sourceFile()))
+ docFiles.append(doc.sourceFile());
+ }
+ writeEnums(s, docPackage.globalEnums, package, &parsedImages);
}
output.done();
+ copyParsedImages(parsedImages, docFiles, QFileInfo(fileName).absolutePath());
}
static inline QString msgNonExistentAdditionalDocFile(const QString &dir,
return result;
}
+// Return rst target document name for additional docs
+// "qtcore/webxml/animation.webxml" -> "qtcore-animation.rst"
+static QString additionalDocRstFileName(const QFileInfo &fi, const QString &rstSuffix)
+{
+ QString result = fi.baseName() + rstSuffix;
+ // Disambiguate file name by directory
+ const QString dirName = fi.absolutePath();
+ QStringView prefix{dirName};
+ if (prefix.endsWith("/webxml"_L1))
+ prefix = prefix.chopped(7);
+ auto lastSlash = prefix.lastIndexOf(u'/');
+ if (lastSlash != -1) {
+ prefix = prefix.sliced(lastSlash + 1);
+ if (!result.startsWith(prefix)) {
+ result.prepend(u'-');
+ result.prepend(prefix);
+ }
+ }
+ return result;
+}
+
void QtDocGenerator::writeAdditionalDocumentation() const
{
QFile additionalDocumentationFile(m_options.additionalDocumentationList);
// Normal file entry
QFileInfo fi(m_options.parameters.docDataDir + u'/' + line);
if (fi.isFile()) {
- const QString rstFileName = fi.baseName() + rstSuffix;
+ const QString rstFileName = additionalDocRstFileName(fi, rstSuffix);
const QString rstFile = targetDir + u'/' + rstFileName;
const QString context = targetDir.mid(targetDir.lastIndexOf(u'/') + 1);
if (convertToRst(fi.absoluteFilePath(),
successCount, count);
}
-#ifdef __WIN32__
-# define PATH_SEP ';'
-#else
-# define PATH_SEP ':'
-#endif
-
bool QtDocGenerator::doSetup()
{
if (m_options.parameters.codeSnippetDirs.isEmpty()) {
m_options.parameters.codeSnippetDirs =
- m_options.parameters.libSourceDir.split(QLatin1Char(PATH_SEP));
+ m_options.parameters.libSourceDir.split(QDir::listSeparator());
}
if (m_docParser.isNull()) {
}
m_docParser->setDocumentationDataDirectory(m_options.parameters.docDataDir);
- m_docParser->setLibrarySourceDirectory(m_options.parameters.libSourceDir);
m_options.parameters.outputDirectory = outputDirectory();
return true;
}
return true;
}
if (key == u"documentation-code-snippets-dir") {
- m_options->parameters.codeSnippetDirs = value.split(QLatin1Char(PATH_SEP));
+ m_options->parameters.codeSnippetDirs = value.split(QDir::listSeparator());
return true;
}
*errorMessage = msgCannotOpenForReading(sourceFile);
return false;
}
- const QString doc = QString::fromUtf8(sourceFile.readAll());
- sourceFile.close();
FileOut targetFile(targetFileName);
- QtXmlToSphinx x(this, m_options.parameters, doc, context);
+ QtXmlToSphinx x(this, m_options.parameters, sourceFile, context);
targetFile.stream << x;
+ copyParsedImages(x.images(), {sourceFileName},
+ QFileInfo(targetFileName).absolutePath());
targetFile.done();
return true;
}
}
return resolved;
}
+
+// Determine image target directory from context,
+// "Pyside2.QtGui.QPainter" ->"Pyside2/QtGui" for the rare case of resolving
+// an image by scope/module.
+// FIXME: Not perfect yet, should have knowledge about namespaces (DataVis3D) or
+// nested classes "Pyside2.QtGui.QTouchEvent.QTouchPoint".
+static QString imageRelativeTargetDirFromContext(const QString &scope,
+ const QString &hrefBase)
+{
+ QString relativeTargetDir = scope;
+ const auto lastDot = relativeTargetDir.lastIndexOf(u'.');
+ if (lastDot != -1)
+ relativeTargetDir.truncate(lastDot);
+ relativeTargetDir.replace(u'.', u'/');
+ relativeTargetDir += hrefBase;
+ return relativeTargetDir;
+}
+
+ResolvedDocImage
+QtDocGenerator::resolveImage(const QtXmlToSphinxImage &image,
+ const QStringList &sourceDirs,
+ const QString &targetDir) const
+{
+ QString hrefBase;
+ QString hrefName = image.href; // split "images/a.png"
+ if (const auto lastSlash = image.href.lastIndexOf(u'/'); lastSlash != -1) {
+ hrefName = image.href.sliced(lastSlash + 1);
+ hrefBase = u'/' + image.href.sliced(0, lastSlash);
+ }
+
+ QStringList candidates;
+ // 1st: Resolve relative to document
+ for (const auto &sourceDir : sourceDirs) {
+ candidates.append(sourceDir + u'/' + image.href);
+ if (QFileInfo::exists(candidates.constLast()))
+ return {hrefName, candidates.constLast(), targetDir + hrefBase};
+ }
+
+ // 2nd: Check in docDataDir (doxygen) FIXME PYSIDE 7: Is this still needed?
+ candidates.append(m_options.parameters.docDataDir + u'/' + image.href);
+ if (QFileInfo::exists(candidates.constLast())) {
+ return {hrefName, candidates.constLast(),
+ m_options.parameters.outputDirectory + hrefBase};
+ }
+
+ // 3rd: Try to resolve via scope/module (relative to output directory).
+ // It should rarely happen since resolution against the source file should
+ // mostly work, only for some cases of injected XML documentation.
+ if (!m_options.doxygen && !image.scope.isEmpty()) {
+ candidates.append(m_options.parameters.docDataDir + u'/' +
+ QtDocParser::qdocModuleDir(image.scope) + u'/' + image.href);
+ if (QFileInfo::exists(candidates.constLast())) {
+ return {hrefName, candidates.constLast(),
+ m_options.parameters.outputDirectory + u'/'
+ + imageRelativeTargetDirFromContext(image.scope, hrefBase)};
+ }
+ }
+
+ throw Exception(msgCannotFindImage(image.href, image.scope, candidates));
+ return {};
+}
+
+static constexpr Qt::CaseSensitivity fileSysCaseSensitivity =
+ QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Windows
+ ? Qt::CaseInsensitive : Qt::CaseSensitive;
+
+// Poor man's relative-to-dir function.
+static QString relativeTo(const QString &path, const QString &dir)
+{
+ const auto dirSize = dir.size();
+ const auto pathSize = path.size();
+ if (dirSize == pathSize && path.compare(dir, fileSysCaseSensitivity) == 0)
+ return {};
+ if (path.size() > dirSize && path.startsWith(dir, fileSysCaseSensitivity)
+ && path.at(dirSize) == u'/') {
+ return path.sliced(dirSize + 1);
+ }
+
+ throw Exception(msgNotRelative(path, dir));
+ return {};
+}
+
+// Copy a parsed image from WebXML to doc/base, creating all needed directories
+static void copyParsedImage(const ResolvedDocImage &image, QDir &targetDir)
+{
+ const QString targetFileName = image.targetDir + u'/' + image.name;
+ if (QFileInfo::exists(targetFileName))
+ return;
+
+ if (!QFileInfo::exists(image.targetDir)
+ && !targetDir.mkpath(relativeTo(image.targetDir, targetDir.absolutePath()))) {
+ throw Exception(msgCannotCreateDir(image.targetDir));
+ }
+ QFile source(image.absoluteSourceFilePath);
+ if (!source.copy(targetFileName))
+ throw Exception(msgCannotCopy(source, targetFileName));
+}
+
+// Copy parsed images from WebXML to doc/base
+void QtDocGenerator::copyParsedImages(const QtXmlToSphinxImages &images,
+ const QStringList &sourceDocumentFiles,
+ const QString &targetDocumentDir) const
+{
+ if (images.isEmpty())
+ return;
+
+ QStringList sourceDirs;
+ for (const auto &sourceDocumentFile : sourceDocumentFiles) {
+ const QString sourceDir = QFileInfo(sourceDocumentFile).absolutePath();
+ if (!sourceDirs.contains(sourceDir))
+ sourceDirs.append(sourceDir);
+ }
+
+ QDir targetDir(targetDocumentDir);
+
+ for (const auto &image : images)
+ copyParsedImage(resolveImage(image, sourceDirs, targetDocumentDir), targetDir);
+}
struct GeneratorDocumentation;
struct DocPackage;
+struct ResolvedDocImage;
+
/**
* The DocGenerator generates documentation from library being binded.
*/
Q_DISABLE_COPY_MOVE(QtDocGenerator)
QtDocGenerator();
- ~QtDocGenerator();
+ ~QtDocGenerator() override;
bool doSetup() override;
bool shouldGenerate(const TypeEntryCPtr &) const override;
static QString fileNameSuffix();
QString fileNameForContext(const GeneratorContext &context) const override;
- void generateClass(TextStream &ts, const GeneratorContext &classContext) override;
+ void generateClass(TextStream &ts, const QString &targetDir,
+ const GeneratorContext &classContext,
+ QList<GeneratorContext> *contexts) override;
bool finishGeneration() override;
private:
+ void generateClassRecursion(TextStream &s, const QString &targetDir,
+ const GeneratorContext &classContext,
+ QList<GeneratorContext> *contexts);
+ void doGenerateClass(TextStream &ts, const QString &targetDir,
+ const AbstractMetaClassCPtr &metaClass);
void writeEnums(TextStream &s, const AbstractMetaEnumList &enums,
- const QString &scope) const;
+ const QString &scope, QtXmlToSphinxImages *images) const;
- void writeFields(TextStream &s, const AbstractMetaClassCPtr &cppClass) const;
+ void writeFields(TextStream &s, const AbstractMetaClassCPtr &cppClass,
+ QtXmlToSphinxImages *images) const;
void writeFunctions(TextStream &s, const AbstractMetaFunctionCList &funcs,
- const AbstractMetaClassCPtr &cppClass, const QString &scope);
+ const AbstractMetaClassCPtr &cppClass, const QString &scope,
+ QtXmlToSphinxImages *images) const;
void writeFunction(TextStream &s, const AbstractMetaFunctionCPtr &func,
+ QtXmlToSphinxImages *images,
const AbstractMetaClassCPtr &cppClass = {},
- const QString &scope = {}, bool indexed = true);
+ const QString &scope = {}, bool indexed = true) const;
void writeFunctionDocumentation(TextStream &s, const AbstractMetaFunctionCPtr &func,
const DocModificationList &modifications,
- const QString &scope) const;
+ const QString &scope,
+ QtXmlToSphinxImages *images) const;
void writeFunctionParametersType(TextStream &s, const AbstractMetaClassCPtr &cppClass,
const AbstractMetaFunctionCPtr &func) const;
static void writeFunctionToc(TextStream &s, const QString &title,
const GeneratorDocumentation &doc);
void writeProperties(TextStream &s,
const GeneratorDocumentation &doc,
- const AbstractMetaClassCPtr &cppClass) const;
+ const AbstractMetaClassCPtr &cppClass,
+ QtXmlToSphinxImages *images) const;
void writeParameterType(TextStream &s, const AbstractMetaClassCPtr &cppClass,
const AbstractMetaArgument &arg) const;
+ void writeFormattedText(TextStream &s, const DocModification &mod,
+ const QString &scope, QtXmlToSphinxImages *images) const;
void writeFormattedText(TextStream &s, const QString &doc,
- Documentation::Format format,
- const QString &scope = {}) const;
+ DocumentationFormat format,
+ const QString &scope,
+ QtXmlToSphinxImages *images) const;
void writeFormattedBriefText(TextStream &s, const Documentation &doc,
- const QString &scope = {}) const;
+ const QString &scope, QtXmlToSphinxImages *images) const;
void writeFormattedDetailedText(TextStream &s, const Documentation &doc,
- const QString &scope = {}) const;
+ const QString &scope,
+ QtXmlToSphinxImages *images) const;
bool writeInjectDocumentation(TextStream &s, TypeSystem::DocModificationMode mode,
- const AbstractMetaClassCPtr &cppClass) const;
+ const AbstractMetaClassCPtr &cppClass,
+ QtXmlToSphinxImages *images) const;
bool writeInjectDocumentation(TextStream &s, TypeSystem::DocModificationMode mode,
const DocModificationList &modifications,
const AbstractMetaFunctionCPtr &func,
- const QString &scope = {}) const;
+ const QString &scope,
+ QtXmlToSphinxImages *images) const;
bool writeDocModifications(TextStream &s, const DocModificationList &mods,
TypeSystem::DocModificationMode mode,
- const QString &scope = {}) const;
+ const QString &scope,
+ QtXmlToSphinxImages *images) const;
static void writeDocSnips(TextStream &s, const CodeSnipList &codeSnips,
TypeSystem::CodeSnipPosition position, TypeSystem::Language language);
const DocPackage &docPackage);
void writeAdditionalDocumentation() const;
bool writeInheritanceFile();
-
+ ResolvedDocImage resolveImage(const QtXmlToSphinxImage &image,
+ const QStringList &sourceDirs,
+ const QString &targetDir) const;
+ void copyParsedImages(const QtXmlToSphinxImages &images,
+ const QStringList &sourceDocumentFiles,
+ const QString &targetDocumentFile) const;
QString translateToPythonType(const AbstractMetaType &type,
const AbstractMetaClassCPtr &cppClass,
bool createRef = true) const;
using namespace Qt::StringLiterals;
+QDebug operator<<(QDebug debug, const QtXmlToSphinxImage &i)
+{
+ QDebugStateSaver saver(debug);
+ debug.noquote();
+ debug.nospace();
+ debug << "QtXmlToSphinxImage(\"" << i.href << "\", \"" << i.scope << "\")";
+ return debug;
+}
+
+// Helpers for extracting qdoc snippets "#/// [id]"
+static QString fileNameOfDevice(const QIODevice *inputFile, QAnyStringView defaultName = "<stdin>")
+{
+ if (inputFile != nullptr) {
+ if (const auto *file = qobject_cast<const QFile *>(inputFile))
+ return QDir::toNativeSeparators(file->fileName());
+ }
+ return defaultName.toString();
+}
+
QString msgTagWarning(const QXmlStreamReader &reader, const QString &context,
const QString &tag, const QString &message)
{
QtXmlToSphinx::QtXmlToSphinx(const QtXmlToSphinxDocGeneratorInterface *docGenerator,
const QtXmlToSphinxParameters ¶meters,
- const QString& doc, const QString& context)
- : m_output(static_cast<QString *>(nullptr)),
- m_context(context),
- m_generator(docGenerator), m_parameters(parameters)
+ const QString& doc, const QString& context) :
+ QtXmlToSphinx(docGenerator, parameters, context)
{
m_result = transform(doc);
}
+QtXmlToSphinx::QtXmlToSphinx(const QtXmlToSphinxDocGeneratorInterface *docGenerator,
+ const QtXmlToSphinxParameters ¶meters,
+ QIODevice &ioDevice,
+ const QString& context) :
+ QtXmlToSphinx(docGenerator, parameters, context)
+{
+ QXmlStreamReader reader(&ioDevice);
+ m_result = transform(reader);
+}
+
+QtXmlToSphinx::QtXmlToSphinx(const QtXmlToSphinxDocGeneratorInterface *docGenerator,
+ const QtXmlToSphinxParameters ¶meters,
+ const QString& context)
+ : m_output(static_cast<QString *>(nullptr)),
+ m_context(context),
+ m_generator(docGenerator), m_parameters(parameters)
+{
+}
+
QtXmlToSphinx::~QtXmlToSphinx() = default;
void QtXmlToSphinx::callHandler(WebXmlTag t, QXmlStreamReader &r)
if (doc.trimmed().isEmpty())
return doc;
- pushOutputBuffer();
-
QXmlStreamReader reader(doc);
+ return transform(reader);
+}
+
+QString QtXmlToSphinx::transform(QXmlStreamReader& reader)
+{
+ pushOutputBuffer();
m_output << autoTranslatedPlaceholder;
Indentation indentation(m_output);
QXmlStreamReader::TokenType token = reader.readNext();
if (reader.hasError()) {
QString message;
- QTextStream(&message) << "XML Error "
- << reader.errorString() << " at " << reader.lineNumber()
- << ':' << reader.columnNumber() << '\n' << doc;
- m_output << message;
+ QTextStream(&message) << fileNameOfDevice(reader.device())
+ << ':' << reader.lineNumber() << ':' << reader.columnNumber()
+ << ": XML Error: " << reader.errorString();
throw Exception(message);
break;
}
Q_ASSERT(m_buffers.isEmpty());
setAutoTranslatedNote(&retval);
return retval;
+
}
static QString resolveFile(const QStringList &locations, const QString &path)
return {{}, Snippet::Error};
}
-// Helpers for extracting qdoc snippets "#/// [id]"
-static QString fileNameOfDevice(const QIODevice *inputFile)
-{
- const auto *file = qobject_cast<const QFile *>(inputFile);
- return file ? QDir::toNativeSeparators(file->fileName()) : u"<stdin>"_s;
-}
-
static QString msgSnippetNotFound(const QIODevice &inputFile,
const QString &identifier)
{
}
// For the language reference documentation, strip the module name.
// Clear the link text if that matches the function/class/enumeration name.
- const int lastSep = linktext.lastIndexOf(u"::");
+ const auto lastSep = linktext.lastIndexOf(u"::");
if (lastSep != -1)
linktext.remove(0, lastSep + 2);
else
return index >= 0 ? m_tagStack.at(index) : WebXmlTag::Unknown;
}
-// Copy images that are placed in a subdirectory "images" under the webxml files
-// by qdoc to a matching subdirectory under the "rst/PySide6/<module>" directory
-static bool copyImage(const QString &href, const QString &docDataDir,
- const QString &context, const QString &outputDir,
- const QLoggingCategory &lc, QString *errorMessage)
-{
- const QChar slash = u'/';
- const auto lastSlash = href.lastIndexOf(slash);
- const QString imagePath = lastSlash != -1 ? href.left(lastSlash) : QString();
- const QString imageFileName = lastSlash != -1 ? href.right(href.size() - lastSlash - 1) : href;
- QFileInfo imageSource(docDataDir + slash + href);
- if (!imageSource.exists()) {
- QTextStream(errorMessage) << "Image " << href << " does not exist in "
- << QDir::toNativeSeparators(docDataDir);
- return false;
- }
- // Determine directory from context, "Pyside2.QtGui.QPainter" ->"Pyside2/QtGui".
- // FIXME: Not perfect yet, should have knowledge about namespaces (DataVis3D) or
- // nested classes "Pyside2.QtGui.QTouchEvent.QTouchPoint".
- QString relativeTargetDir = context;
- const auto lastDot = relativeTargetDir.lastIndexOf(u'.');
- if (lastDot != -1)
- relativeTargetDir.truncate(lastDot);
- relativeTargetDir.replace(u'.', slash);
- if (!imagePath.isEmpty())
- relativeTargetDir += slash + imagePath;
-
- const QString targetDir = outputDir + slash + relativeTargetDir;
- const QString targetFileName = targetDir + slash + imageFileName;
- if (QFileInfo::exists(targetFileName))
- return true;
- if (!QFileInfo::exists(targetDir)) {
- const QDir outDir(outputDir);
- if (!outDir.mkpath(relativeTargetDir)) {
- QTextStream(errorMessage) << "Cannot create " << QDir::toNativeSeparators(relativeTargetDir)
- << " under " << QDir::toNativeSeparators(outputDir);
- return false;
- }
- }
-
- QFile source(imageSource.absoluteFilePath());
- if (!source.copy(targetFileName)) {
- QTextStream(errorMessage) << "Cannot copy " << QDir::toNativeSeparators(source.fileName())
- << " to " << QDir::toNativeSeparators(targetFileName) << ": "
- << source.errorString();
- return false;
- }
- qCDebug(lc).noquote().nospace() << __FUNCTION__ << " href=\""
- << href << "\", context=\"" << context << "\", docDataDir=\""
- << docDataDir << "\", outputDir=\"" << outputDir << "\", copied \""
- << source.fileName() << "\"->\"" << targetFileName << '"';
- return true;
-}
-
-bool QtXmlToSphinx::copyImage(const QString &href) const
-{
- QString errorMessage;
- const bool result =
- ::copyImage(href, m_parameters.docDataDir, m_context,
- m_parameters.outputDirectory,
- m_generator->loggingCategory(),
- &errorMessage);
- if (!result)
- throw Exception(errorMessage);
- return result;
-}
-
void QtXmlToSphinx::handleImageTag(QXmlStreamReader& reader)
{
if (reader.tokenType() != QXmlStreamReader::StartElement)
return;
const QString href = reader.attributes().value(u"href"_s).toString();
- if (copyImage(href))
- m_output << ".. image:: " << href << "\n\n";
+ m_images.append({m_context, href});
+ m_output << ".. image:: " << href << "\n\n";
}
void QtXmlToSphinx::handleInlineImageTag(QXmlStreamReader& reader)
if (reader.tokenType() != QXmlStreamReader::StartElement)
return;
const QString href = reader.attributes().value(u"href"_s).toString();
- if (!copyImage(href))
- return;
+ m_images.append({m_context, href});
// Handle inline images by substitution references. Insert a unique tag
// enclosed by '|' and define it further down. Determine tag from the base
//file name with number.
class QtXmlToSphinxDocGeneratorInterface;
struct QtXmlToSphinxParameters;
struct QtXmlToSphinxLink;
+struct QtXmlToSphinxImage;
enum class WebXmlTag;
explicit QtXmlToSphinx(const QtXmlToSphinxDocGeneratorInterface *docGenerator,
const QtXmlToSphinxParameters ¶meters,
const QString& doc,
- const QString& context = QString());
+ const QString& context);
+ explicit QtXmlToSphinx(const QtXmlToSphinxDocGeneratorInterface *docGenerator,
+ const QtXmlToSphinxParameters ¶meters,
+ QIODevice &ioDevice,
+ const QString& context);
~QtXmlToSphinx();
- QString result() const
- {
- return m_result;
- }
+ const QString &result() const { return m_result; }
+ const QList<QtXmlToSphinxImage> &images() const { return m_images; }
static void stripPythonQualifiers(QString *s);
QString *errorMessage);
private:
+ explicit QtXmlToSphinx(const QtXmlToSphinxDocGeneratorInterface *docGenerator,
+ const QtXmlToSphinxParameters ¶meters,
+ const QString& context = QString());
+
using StringSharedPtr = std::shared_ptr<QString>;
QString transform(const QString& doc);
+ QString transform(QXmlStreamReader& reader);
void handleHeadingTag(QXmlStreamReader& reader);
void handleParaTag(QXmlStreamReader& reader);
QStack<WebXmlTag> m_tagStack;
TextStream m_output;
QString m_result;
+ QList<QtXmlToSphinxImage> m_images;
QStack<StringSharedPtr> m_buffers; // Maintain address stability since it used in TextStream
void pushOutputBuffer();
QString popOutputBuffer();
void writeTable(Table& table);
- bool copyImage(const QString &href) const;
void callHandler(WebXmlTag t, QXmlStreamReader &);
void formatCurrentTable();
};
#include <QtCore/QStringList>
QT_FORWARD_DECLARE_CLASS(QLoggingCategory)
+QT_FORWARD_DECLARE_CLASS(QDebug)
+
+struct QtXmlToSphinxImage
+{
+ QString scope;
+ QString href;
+};
+
+QDebug operator<<(QDebug debug, const QtXmlToSphinxImage &i);
+
+using QtXmlToSphinxImages = QList<QtXmlToSphinxImage>;
struct QtXmlToSphinxParameters
{
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "cppgenerator.h"
+#include "anystringview_helpers.h"
#include "configurablescope.h"
#include "generatorargument.h"
#include "generatorstrings.h"
static const char shibokenErrorsOccurred[] = "Shiboken::Errors::occurred() != nullptr";
static constexpr auto virtualMethodStaticReturnVar = "result"_L1;
+static constexpr auto initFuncPrefix = "init_"_L1;
static constexpr auto sbkObjectTypeF = "SbkObject_TypeF()"_L1;
+static constexpr auto enumConverterPythonType = "Enum"_L1;
+static constexpr auto flagsConverterPythonType = "Flag"_L1;
static const char initInheritanceFunction[] = "initInheritance";
static QString mangleName(QString name)
case CppGenerator::ErrorReturn::MinusOne:
s << " -1";
break;
+ case CppGenerator::ErrorReturn::NullPtr:
+ s << " nullptr";
+ break;
case CppGenerator::ErrorReturn::Void:
break;
}
static constexpr auto converterVar = "converter"_L1;
-struct registerConverterName
+// Helper streamable class for generating code registering converters
+class registerConverterName
{
+public:
+ enum Flag
+ {
+ Indirections = 0x1, // Also register "Type*", "Type&"
+ PartiallyQualifiedAliases = 0x2, // Also register "B" when passed "A::B"
+ TypeId = 0x4, // Use typeid().name() for the string passed in
+ Alias = 0x8 // This is an alias
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
explicit registerConverterName(QAnyStringView typeName,
- QAnyStringView varName = converterVar) :
- m_typeName(typeName), m_varName(varName) {}
+ QAnyStringView varName = {},
+ Flags flags = {}) noexcept:
+ m_typeName(typeName), m_varName(varName), m_flags(flags) {}
+
+ void format(TextStream &s) const;
+
+ inline friend TextStream &operator<<(TextStream &s, const registerConverterName &r)
+ {
+ r.format(s);
+ return s;
+ }
+
+private:
+ static void formatEntry(TextStream &s, QAnyStringView typeName,
+ QAnyStringView varName, Flags flags,
+ const char *indirection = "");
QAnyStringView m_typeName;
QAnyStringView m_varName;
+ Flags m_flags;
};
-TextStream &operator<<(TextStream &s, const registerConverterName &r)
+Q_DECLARE_OPERATORS_FOR_FLAGS(registerConverterName::Flags)
+
+void registerConverterName::formatEntry(TextStream &s, QAnyStringView typeName,
+ QAnyStringView varName, Flags flags,
+ const char *indirection)
{
- s << "Shiboken::Conversions::registerConverterName(" << r.m_varName
- << ", \"" << r.m_typeName << "\");\n";
- return s;
+ s << "Shiboken::Conversions::registerConverter"
+ << (flags.testFlag(Alias) ? "Alias" : "Name")
+ << '(' << varName << ", ";
+ if (flags.testFlag(TypeId))
+ s << "typeid(" << typeName << indirection << ").name()";
+ else
+ s << '"' << typeName << indirection << '"';
+ s << ");\n";
+}
+
+void registerConverterName::format(TextStream &s) const
+{
+ QAnyStringView typeName = m_typeName;
+ const QAnyStringView varName = m_varName.isEmpty() ? converterVar : m_varName;
+ auto flags = m_flags;
+
+ while (true) {
+ formatEntry(s, typeName, varName, flags);
+ if (flags.testFlag(Indirections)) {
+ formatEntry(s, typeName, varName, flags, "*");
+ formatEntry(s, typeName, varName, flags, "&");
+ }
+
+ if (!flags.testFlag(PartiallyQualifiedAliases))
+ break;
+ auto pos = asv_indexOf(typeName, "::");
+ if (pos < 0)
+ break;
+ typeName = typeName.sliced(pos + 2);
+ flags.setFlag(Alias);
+ }
}
// Protocol function name / function parameters / return type
static bool contains(const ProtocolEntries &l, const QString &needle)
{
- for (const auto &m : l) {
- if (m.name == needle)
- return true;
- }
- return false;
+ return std::any_of(l.cbegin(), l.cend(),
+ [&needle](const ProtocolEntry &e) {
+ return e.name == needle;
+ });
}
// Maps special function names to function parameters and return types
"#endif\n"
"#include <QtCore/QDebug>\n";
+static QString compilerOptionOptimize()
+{
+ static QString result;
+ if (result.isEmpty()) {
+ const auto optimizations = CppGenerator::optimizations();
+ QTextStream str(&result);
+ str << "#define PYSIDE6_COMOPT_FULLNAME "
+ << (optimizations.testFlag(Generator::RemoveFullnameField) ? '1' : '0')
+ << "\n#define PYSIDE6_COMOPT_COMPRESS "
+ << (optimizations.testFlag(Generator::CompressSignatureStrings) ? '1' : '0')
+ << "\n// TODO: #define PYSIDE6_COMOPT_FOLDING "
+ << (optimizations.testFlag(Generator::FoldCommonTailCode) ? '1' : '0') << '\n';
+ }
+ return result;
+}
+
QString CppGenerator::chopType(QString s)
{
if (s.endsWith(u"_Type"))
return s;
}
-static bool isStdSetterName(QString setterName, QString propertyName)
+static bool isStdSetterName(const QString &setterName, const QString &propertyName)
{
return setterName.size() == propertyName.size() + 3
&& setterName.startsWith(u"set")
s << HeaderGenerator::protectedHackDefine;
QByteArrayList cppIncludes{"typeinfo", "iterator", // for containers
- "cctype", "cstring"};
+ "cctype", "cstring",
+ "type_traits"}; // enum/underlying type
// headers
s << "// default includes\n";
s << "#include <shiboken.h>\n";
cppIncludes << "iostream";
}
+ s << '\n' << compilerOptionOptimize() << '\n';
if (normalClass && usePySideExtensions()) {
s << includeQDebug;
if (metaClass->hasToStringCapability())
/// Function used to write the class generated binding code on the buffer
/// \param s the output buffer
/// \param classContext the pointer to metaclass information
-void CppGenerator::generateClass(TextStream &s, const GeneratorContext &classContext)
+void CppGenerator::generateClass(TextStream &s,
+ const QString & /* targetDir */,
+ const GeneratorContext &classContext,
+ QList<GeneratorContext> *)
{
- if (classContext.forSmartPointer()) {
- generateSmartPointerClass(s, classContext);
- return;
- }
-
s.setLanguage(TextStream::Language::Cpp);
AbstractMetaClassCPtr metaClass = classContext.metaClass();
const auto typeEntry = metaClass->typeEntry();
}
int maxOverrides = 0;
- writeCacheResetNative(s, classContext);
+ if (useOverrideCaching(classContext.metaClass()))
+ writeCacheResetNative(s, classContext);
for (const auto &func : metaClass->functions()) {
const auto generation = functionGeneration(func);
if (generation.testFlag(FunctionGenerationFlag::WrapperConstructor))
if (overloads.isEmpty())
continue;
- const auto rfunc = overloads.constFirst();
+ const auto &rfunc = overloads.constFirst();
OverloadData overloadData(overloads, api());
if (rfunc->isConstructor()) {
writeConstructorWrapper(s, overloadData, classContext);
- writeSignatureInfo(signatureStream, overloadData);
+ // On constructors, we also generate the property initializers.
+ writeSignatureInfo(signatureStream, overloadData, true);
}
// call operators
else if (rfunc->name() == u"operator()") {
if (generateRichComparison(classContext)) {
s << "// Rich comparison\n";
- writeRichCompareFunction(s, classContext);
+ writeRichCompareFunction(s, signatureStream, classContext);
}
if (shouldGenerateGetSetList(metaClass)) {
s << closeExternC;
if (hasHashFunction(metaClass))
- writeHashFunction(s, classContext);
+ writeHashFunction(s, signatureStream, classContext);
// Write tp_traverse and tp_clear functions.
writeTpTraverseFunction(s, metaClass);
const QString qualifiedName = classContext.wrapperName() + u"::"_s;
s << functionSignature(func, qualifiedName, QString(),
OriginalTypeDescription | SkipDefaultValues);
- s << " : ";
- writeFunctionCall(s, func);
+ if (!func->arguments().isEmpty()) {
+ s << " : ";
+ writeFunctionCall(s, func);
+ }
s << "\n{\n" << indent;
if (wrapperDiagnostics())
s << R"(std::cerr << __FUNCTION__ << ' ' << this << '\n';)" << '\n';
const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast();
- s << "resetPyMethodCache();\n";
- writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionBeginning,
- TypeSystem::NativeCode, func, false, lastArg);
- s << "// ... middle\n";
- writeCodeSnips(s, func->injectedCodeSnips(), TypeSystem::CodeSnipPositionEnd,
- TypeSystem::NativeCode, func, false, lastArg);
+ const auto &codeSnips = func->injectedCodeSnips();
+ if (!codeSnips.isEmpty()) {
+ writeCodeSnips(s, codeSnips, TypeSystem::CodeSnipPositionBeginning,
+ TypeSystem::NativeCode, func, false, lastArg);
+ s << "// ... middle\n";
+ writeCodeSnips(s, codeSnips, TypeSystem::CodeSnipPositionEnd,
+ TypeSystem::NativeCode, func, false, lastArg);
+ }
s << outdent << "}\n\n";
}
static const QRegularExpression regex("%(\\d+)"_L1);
Q_ASSERT(regex.isValid());
QString expr = argMod.replacedDefaultExpression();
- for (int offset = 0; ; ) {
+ for (qsizetype offset = 0; ; ) {
const QRegularExpressionMatch match = regex.match(expr, offset);
if (!match.hasMatch())
break;
<< indent << argConversions.join(u",\n"_s) << outdent << "\n));\n";
for (int index : std::as_const(invalidateArgs)) {
- s << "bool invalidateArg" << index << " = Py_REFCNT(PyTuple_GET_ITEM(" << PYTHON_ARGS
+ s << "bool invalidateArg" << index << " = Py_REFCNT(PyTuple_GetItem(" << PYTHON_ARGS
<< ", " << index - 1 << ")) == 1;\n";
}
}
writeFuncNameVar(s, func, funcName);
s << "Shiboken::AutoDecRef " << PYTHON_OVERRIDE_VAR
<< "(Shiboken::BindingManager::instance().getOverride(this, nameCache, funcName));\n"
- << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {\n" << indent;
- if (useOverrideCaching(func->ownerClass()))
- s << "m_PyMethodCache[" << cacheIndex << "] = true;\n";
+ << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {\n" << indent
+ << "m_PyMethodCache[" << cacheIndex << "] = true;\n";
writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType,
returnStatement.statement, true);
s << outdent << "}\n\n"; //WS
for (int argIndex : std::as_const(invalidateArgs)) {
s << "if (invalidateArg" << argIndex << ")\n" << indent
- << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" << PYTHON_ARGS
+ << "Shiboken::Object::invalidate(PyTuple_GetItem(" << PYTHON_ARGS
<< ", " << (argIndex - 1) << "));\n" << outdent;
}
if (generateNewCall)
s << "if (" << PYTHON_RETURN_VAR << ".isNull()) {\n" << indent
<< "// An error happened in python code!\n"
- << "Shiboken::Errors::storeErrorOrPrint();\n"
+ << "Shiboken::Errors::storePythonOverrideErrorOrPrint(\""
+ << func->ownerClass()->name() << "\", funcName);\n"
<< returnStatement.statement << "\n" << outdent
<< "}\n";
c << "*reinterpret_cast<" << cppTypeName << " *>(cppOut) = value;\n";
ConfigurableScope configScope(s, enumType);
- writePythonToCppFunction(s, c.toString(), typeName, typeName);
+ writePythonToCppFunction(s, c.toString(), enumConverterPythonType, typeName);
QString pyTypeCheck = u"PyObject_TypeCheck(pyIn, "_s + enumPythonType + u')';
- writeIsPythonConvertibleToCppFunction(s, typeName, typeName, pyTypeCheck);
+ writeIsPythonConvertibleToCppFunction(s, enumConverterPythonType, typeName, pyTypeCheck);
c.clear();
- c << "const int castCppIn = int(*reinterpret_cast<const "
+ c << "using IntType = std::underlying_type_t<" << cppTypeName << ">;\n"
+ "const auto castCppIn = IntType(*reinterpret_cast<const "
<< cppTypeName << " *>(cppIn));\n" << "return "
<< "Shiboken::Enum::newItem(" << enumPythonType << ", castCppIn);\n";
- writeCppToPythonFunction(s, c.toString(), typeName, typeName);
s << '\n';
+ writeCppToPythonFunction(s, c.toString(), typeName, enumConverterPythonType);
+ s << '\n';
+
+ auto flags = enumType->flags();
+ if (!flags)
+ return;
+ QString flagsType = "QFlags<"_L1 + cppTypeName + u'>';
+
+ c.clear();
+ c << "const auto value = "<< flagsType << "::fromInt(Shiboken::Enum::getValue(pyIn));\n"
+ << "*reinterpret_cast<" << flagsType << " *>(cppOut) = value;\n";
+ writePythonToCppFunction(s, c.toString(), flagsConverterPythonType, typeName);
+
+ c.clear();
+ c << "const auto flags = *reinterpret_cast<const " << flagsType
+ << " *>(cppIn);\n"
+ << "return Shiboken::Enum::newItem(" << enumPythonType << ", flags.toInt());\n";
+ s << '\n';
+ writeCppToPythonFunction(s, c.toString(), typeName, flagsConverterPythonType);
+ s << '\n';
+}
+
+static void writePointerToPythonConverter(TextStream &c,
+ const AbstractMetaClassCPtr &metaClass,
+ const QString &typeName,
+ const QString &cpythonType)
+{
+ c << "auto *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n"
+ << "if (pyOut) {\n" << indent
+ << "Py_INCREF(pyOut);\nreturn pyOut;\n" << outdent
+ << "}\n";
+
+ const QString nameFunc = metaClass->typeEntry()->polymorphicNameFunction();
+ if (nameFunc.isEmpty() && !metaClass->hasVirtualDestructor()) {
+ c << "return Shiboken::Object::newObjectWithHeuristics("
+ << cpythonType << ", const_cast<void *>(cppIn), false);\n";
+ return;
+ }
+
+ c << "auto *tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn);
+const char *typeName = )";
+ if (nameFunc.isEmpty())
+ c << "typeid(*tCppIn).name();\n";
+ else
+ c << nameFunc << "(tCppIn);\n";
+ c << "return Shiboken::Object::newObjectForPointer("
+ << cpythonType << ", const_cast<void *>(cppIn), false, typeName);\n";
}
void CppGenerator::writeConverterFunctions(TextStream &s, const AbstractMetaClassCPtr &metaClass,
c << "return PySide::getWrapperForQObject(reinterpret_cast<"
<< typeName << " *>(const_cast<void *>(cppIn)), " << cpythonType << ");\n";
} else {
- c << "auto *pyOut = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(cppIn));\n"
- << "if (pyOut) {\n" << indent
- << "Py_INCREF(pyOut);\nreturn pyOut;\n" << outdent
- << "}\n"
- << "bool exactType = false;\n"
- << "auto *tCppIn = reinterpret_cast<const " << typeName << R"( *>(cppIn);
-const char *typeName = )";
-
- const QString nameFunc = metaClass->typeEntry()->polymorphicNameFunction();
- if (nameFunc.isEmpty())
- c << "typeid(*tCppIn).name();\n";
- else
- c << nameFunc << "(tCppIn);\n";
- c << R"(auto *sbkType = Shiboken::ObjectType::typeForTypeName(typeName);
-if (sbkType != nullptr && Shiboken::ObjectType::hasSpecialCastFunction(sbkType))
- exactType = true;
-)"
- << "PyObject *result = Shiboken::Object::newObject(" << cpythonType
- << R"(, const_cast<void *>(cppIn), false, exactType, typeName);
-return result;)";
+ writePointerToPythonConverter(c, metaClass, typeName, cpythonType);
}
std::swap(targetTypeName, sourceTypeName);
writeCppToPythonFunction(s, c.toString(), sourceTypeName, targetTypeName);
}
s << outdent << ");\n\n";
- auto writeConversions = [&s](const QString &signature)
- {
- s << registerConverterName(signature) << registerConverterName(signature + u'*')
- << registerConverterName(signature + u'&');
- };
-
- auto writeConversionsForType = [writeConversions](const QString &fullTypeName)
- {
- QStringList lst = fullTypeName.split(u"::"_s,
- Qt::SkipEmptyParts);
- while (!lst.isEmpty()) {
- QString signature = lst.join(u"::"_s);
- writeConversions(signature);
- lst.removeFirst();
- }
- };
-
-
if (!classContext.forSmartPointer()) {
- writeConversionsForType(metaClass->qualifiedCppName());
+ s << registerConverterName(metaClass->qualifiedCppName(), {},
+ registerConverterName::Indirections
+ | registerConverterName::PartiallyQualifiedAliases);
} else {
- const QString &smartPointerType = classContext.preciseType().instantiations().at(0).cppSignature();
+ QString pointeeType = classContext.preciseType().instantiations().at(0).cppSignature();
const QString &smartPointerName = classContext.preciseType().typeEntry()->name();
- QStringList lst = smartPointerType.split(u"::"_s,
- Qt::SkipEmptyParts);
- while (!lst.isEmpty()) {
- QString signature = lst.join(u"::"_s);
- writeConversions(smartPointerName + u'<' + signature + u'>');
- lst.removeFirst();
+ registerConverterName::Flags flags = registerConverterName::Indirections;
+ while (true) {
+ s << registerConverterName(smartPointerName + u'<' + pointeeType + u'>', {}, flags);
+ auto pos = pointeeType.indexOf("::"_L1);
+ if (pos < 0)
+ break;
+ pointeeType.remove(0, pos + 2);
+ flags.setFlag(registerConverterName::Alias);
}
-
- writeConversionsForType(smartPointerType);
}
- s << "Shiboken::Conversions::registerConverterName(converter, typeid(" << m_gsp;
- QString qualifiedCppNameInvocation;
+ QString qualifiedCppNameInvocation = m_gsp;
if (!classContext.forSmartPointer())
- qualifiedCppNameInvocation = metaClass->qualifiedCppName();
+ qualifiedCppNameInvocation += metaClass->qualifiedCppName();
else
- qualifiedCppNameInvocation = classContext.preciseType().cppSignature();
-
- s << qualifiedCppNameInvocation << ").name());\n";
+ qualifiedCppNameInvocation += classContext.preciseType().cppSignature();
+ s << registerConverterName(qualifiedCppNameInvocation, {},
+ registerConverterName::TypeId);
if (classContext.useWrapper()) {
- s << "Shiboken::Conversions::registerConverterName(converter, typeid("
- << classContext.wrapperName() << ").name());\n";
+ s << registerConverterName(classContext.wrapperName(), {},
+ registerConverterName::TypeId);
}
if (!typeEntry->isValue() && !typeEntry->isSmartPointer())
targetTypeName = sourceTypeName + u"_COPY"_s;
QString toCpp = pythonToCppFunctionName(sourceTypeName, targetTypeName);
QString isConv = convertibleToCppFunctionName(sourceTypeName, targetTypeName);
- writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv);
+ writeAddPythonToCppConversion(s, converterVar, toCpp, isConv);
// User provided implicit conversions.
}
QString toCpp = pythonToCppFunctionName(sourceType, targetType);
QString isConv = convertibleToCppFunctionName(sourceType, targetType);
- writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv);
+ writeAddPythonToCppConversion(s, converterVar, toCpp, isConv);
}
if (typeEntry->isValue()) {
&& isQObject(rfunc->ownerClass());
}
+static bool canAvoidFullname(const GeneratorContext &context)
+{
+ // PYSIDE-2701: Make fullname suppression configurable.
+ return Generator::optimizations().testFlag(Generator::RemoveFullnameField)
+ && context.hasClass();
+}
+
void CppGenerator::writeMethodWrapperPreamble(TextStream &s,
const OverloadData &overloadData,
const GeneratorContext &context,
ErrorReturn errorReturn)
{
const auto rfunc = overloadData.referenceFunction();
- const auto ownerClass = rfunc->targetLangOwner();
- Q_ASSERT(ownerClass == context.metaClass());
int minArgs = overloadData.minArgs();
int maxArgs = overloadData.maxArgs();
- bool initPythonArguments;
+ bool initPythonArguments{};
// If method is a constructor...
if (rfunc->isConstructor()) {
+ const auto ownerClass = rfunc->targetLangOwner();
+ Q_ASSERT(ownerClass == context.metaClass());
// Check if the right constructor was called.
if (!ownerClass->hasPrivateDestructor()) {
s << "if (Shiboken::Object::isUserType(self) && "
if (needsArgumentErrorHandling(overloadData))
s << "Shiboken::AutoDecRef errInfo{};\n";
- s << "static const char fullName[] = \"" << fullPythonFunctionName(rfunc, true)
- << "\";\nSBK_UNUSED(fullName)\n"
- << "Shiboken::PythonContextMarker pcm;\n";
+ bool needsFullName = !canAvoidFullname(context);
+ if (needsFullName)
+ s << "static const char fullName[] = \"" << fullPythonFunctionName(rfunc, true)
+ << "\";\nSBK_UNUSED(fullName)\n";
+ s << "Shiboken::PythonContextMarker pcm;\n";
// PYSIDE-2335: Mark blocking calls like `exec` or `run` as such.
bool isBlockingFunction = rfunc->name() == u"exec"_s || rfunc->name() == u"exec_"_s
|| rfunc->name() == u"run"_s;
&& !overloadData.pythonFunctionWrapperUsesListOfArguments()) {
s << "(" << PYTHON_ARG << " == 0 ? 0 : 1);\n";
} else {
- writeArgumentsInitializer(s, overloadData, errorReturn);
+ writeArgumentsInitializer(s, overloadData, context, errorReturn);
}
}
}
const auto metaClass = rfunc->ownerClass();
s << "static int\n";
- s << cpythonFunctionName(rfunc)
+ s << cpythonConstructorName(metaClass)
<< "(PyObject *self, PyObject *args, PyObject *kwds)\n{\n" << indent;
if (overloadData.maxArgs() == 0 || metaClass->isAbstract())
s << sbkUnusedVariableCast("args");
s << sbkUnusedVariableCast("kwds");
const bool needsMetaObject = usePySideExtensions() && isQObject(metaClass);
- if (needsMetaObject)
- s << "const QMetaObject *metaObject;\n";
s << "auto *sbkSelf = reinterpret_cast<SbkObject *>(self);\n";
s << sbkUnusedVariableCast("sbkSelf")
<< sbkUnusedVariableCast("type")
<< sbkUnusedVariableCast("myType");
- if (needsMetaObject)
- s << sbkUnusedVariableCast("metaObject");
s << "Shiboken::Errors::setInstantiateAbstractClassDisabledWrapper(\""
<< metaClass->qualifiedCppName() << "\");\n" << errorReturn << outdent
<< "}\n\n";
s << '\n';
if (overloadData.maxArgs() > 0)
- writeOverloadedFunctionDecisor(s, overloadData, errorReturn);
+ writeOverloadedFunctionDecisor(s, overloadData, classContext, errorReturn);
// Handles Python Multiple Inheritance
QString pre = needsMetaObject ? u"bool usesPyMI = "_s : u""_s;
s << "\n// PyMI support\n"
- << pre << "Shiboken::callInheritedInit(self, args, kwds, fullName);\n"
- << "if (" << shibokenErrorsOccurred << ")\n"
+ << pre << "Shiboken::callInheritedInit(self, args, kwds, "
+ << (canAvoidFullname(classContext) ? typeInitStruct(classContext) : "fullName"_L1)
+ << ");\nif (" << shibokenErrorsOccurred << ")\n"
<< indent << errorReturn << outdent << "\n";
writeFunctionCalls(s, overloadData, classContext, errorReturn);
<< "}\n";
if (overloadData.maxArgs() > 0)
s << "if (cptr == nullptr)\n" << indent
- << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n\n"
- << outdent;
+ << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn)
+ << ";\n\n" << outdent;
s << "Shiboken::Object::setValidCpp(sbkSelf, true);\n";
// If the created C++ object has a C++ wrapper the ownership is assigned to Python
if (needsMetaObject) {
s << "\n// QObject setup\n"
<< "PySide::Signal::updateSourceObject(self);\n"
- << "metaObject = cptr->metaObject(); // <- init python qt properties\n"
+ << "const auto *metaObject = cptr->metaObject(); // <- init python qt properties\n"
<< "if (!errInfo.isNull() && PyDict_Check(errInfo.object())) {\n" << indent
<< "if (!PySide::fillQtProperties(self, metaObject, errInfo, usesPyMI))\n" << indent
- << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
- << outdent << outdent
+ << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn)
+ << ";\n" << outdent << outdent
<< "};\n";
}
<< "// Do not enter here if other object has implemented a reverse operator.\n"
<< "if (" << PYTHON_RETURN_VAR << " == nullptr) {\n" << indent;
if (maxArgs > 0)
- writeOverloadedFunctionDecisor(s, overloadData, ErrorReturn::Default);
+ writeOverloadedFunctionDecisor(s, overloadData, classContext, ErrorReturn::Default);
writeFunctionCalls(s, overloadData, classContext, ErrorReturn::Default);
s << outdent << '\n' << "} // End of \"if (!" << PYTHON_RETURN_VAR << ")\"\n";
} else { // binary shift operator
if (maxArgs > 0)
- writeOverloadedFunctionDecisor(s, overloadData, ErrorReturn::Default);
+ writeOverloadedFunctionDecisor(s, overloadData, classContext, ErrorReturn::Default);
writeFunctionCalls(s, overloadData, classContext, ErrorReturn::Default);
}
}
void CppGenerator::writeArgumentsInitializer(TextStream &s, const OverloadData &overloadData,
+ const GeneratorContext &classContext,
ErrorReturn errorReturn)
{
const auto rfunc = overloadData.referenceFunction();
- s << "PyTuple_GET_SIZE(args);\n" << sbkUnusedVariableCast("numArgs");
+ s << "PyTuple_Size(args);\n" << sbkUnusedVariableCast("numArgs");
int minArgs = overloadData.minArgs();
int maxArgs = overloadData.maxArgs();
s << "errInfo.reset(Shiboken::checkInvalidArgumentCount(numArgs, "
<< minArgs << ", " << maxArgs << "));\n"
<< "if (!errInfo.isNull())\n" << indent
- << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
+ << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn) << ";\n"
<< outdent;
}
s << "numArgs == " << invalidArgsLength.at(i);
}
s << ")\n" << indent
- << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
+ << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn) << ";\n"
<< outdent;
}
s << '\n';
}
QString CppGenerator::returnErrorWrongArguments(const OverloadData &overloadData,
- ErrorReturn errorReturn)
+ const GeneratorContext &context,
+ ErrorReturn errorReturn)
{
+ Q_UNUSED(context);
const auto rfunc = overloadData.referenceFunction();
+ QString exprRest;
+ if (canAvoidFullname(context)) {
+ const QString &name = rfunc->isConstructor() ? "__init__"_L1 : rfunc->name();
+ exprRest = ", \""_L1 + name + "\", errInfo, "_L1 + typeInitStruct(context) + ")"_L1;
+ } else {
+ exprRest = ", fullName, errInfo)"_L1;
+ }
QString argsVar = overloadData.pythonFunctionWrapperUsesListOfArguments()
? u"args"_s : PYTHON_ARG;
switch (errorReturn) {
case ErrorReturn::Default:
- return u"Shiboken::returnWrongArguments("_s + argsVar + u", fullName, errInfo)"_s;
+ case ErrorReturn::NullPtr:
+ return u"Shiboken::returnWrongArguments("_s + argsVar + exprRest;
case ErrorReturn::Zero:
- return u"Shiboken::returnWrongArguments_Zero("_s + argsVar + u", fullName, errInfo)"_s;
+ return u"Shiboken::returnWrongArguments_Zero("_s + argsVar + exprRest;
case ErrorReturn::MinusOne:
- return u"Shiboken::returnWrongArguments_MinusOne("_s + argsVar + u", fullName, errInfo)"_s;
+ return u"Shiboken::returnWrongArguments_MinusOne("_s + argsVar + exprRest;
case ErrorReturn::Void:
Q_ASSERT(false);
}
// This condition trusts that the OverloadData object will arrange for
// PyLong type to come after the more precise numeric types (e.g. float and bool)
AbstractMetaType argType = overloadData->modifiedArgType();
- if (auto viewOn = argType.viewOn())
+ if (const auto *viewOn = argType.viewOn())
argType = *viewOn;
const bool numberType = numericTypes.size() == 1 || ShibokenGenerator::isPyInt(argType);
bool rejectNull =
+ QString::number(nestedArrayTypes.constFirst().arrayElementCount())
+ u'>';
}
- return QString();
+ return {};
}
// Helper to write argument initialization code for a function argument
void CppGenerator::writeOverloadedFunctionDecisor(TextStream &s,
const OverloadData &overloadData,
+ const GeneratorContext &classContext,
ErrorReturn errorReturn) const
{
s << "// Overloaded function decisor\n";
const auto rfunc = overloadData.referenceFunction();
const AbstractMetaFunctionCList &functionOverloads = overloadData.overloads();
for (qsizetype i = 0; i < functionOverloads.size(); ++i) {
- const auto func = functionOverloads.at(i);
+ const auto &func = functionOverloads.at(i);
s << "// " << i << ": ";
if (func->isStatic())
s << "static ";
s << "// Function signature not found.\n"
<< "if (overloadId == -1)\n" << indent
- << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n\n"
- << outdent;
+ << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn)
+ << ";\n\n" << outdent;
}
void CppGenerator::writeOverloadedFunctionDecisorEngine(TextStream &s,
errorReturn);
} else {
for (qsizetype i = 0; i < overloads.size(); ++i) {
- const auto func = overloads.at(i);
+ const auto &func = overloads.at(i);
s << "case " << i << ": // " << func->signature() << "\n{\n" << indent;
writeSingleFunctionCall(s, overloadData, func, context, errorReturn);
s << "break;\n" << outdent << "}\n";
const bool usePyArgs = overloadData.pythonFunctionWrapperUsesListOfArguments();
// Handle named arguments.
- writeNamedArgumentResolution(s, func, usePyArgs, overloadData, errorReturn);
+ writeNamedArgumentResolution(s, func, usePyArgs, overloadData, context, errorReturn);
bool injectCodeCallsFunc = injectedCodeCallsCppFunction(context, func);
bool mayHaveUnunsedArguments = !func->isUserAdded() && func->hasInjectedCode() && injectCodeCallsFunc;
int numRemovedArgs = OverloadData::numberOfRemovedArguments(func);
- s << "if (Shiboken::Errors::occurred() == nullptr) {\n" << indent;
+ const bool hasArguments = !func->arguments().isEmpty();
+ if (hasArguments) // Argument conversion error?
+ s << "if (Shiboken::Errors::occurred() == nullptr) {\n" << indent;
writeMethodCall(s, func, context,
overloadData.pythonFunctionWrapperUsesListOfArguments(),
func->arguments().size() - numRemovedArgs, indirections, errorReturn);
if (!func->isConstructor())
writeNoneReturn(s, func, overloadData.hasNonVoidReturnType());
- s << outdent << "}\n";
+ if (hasArguments)
+ s << outdent << "}\n";
}
QString CppGenerator::cppToPythonFunctionName(const QString &sourceTypeName, QString targetTypeName)
// Containers of opaque containers are not handled here.
const auto generatorArg = GeneratorArgument::fromMetaType(type);
if (generatorArg.indirections > 0 && !type.generateOpaqueContainer()) {
- for (int pos = 0; ; ) {
+ for (qsizetype pos = 0; ; ) {
const QRegularExpressionMatch match = convertToCppRegEx().match(code, pos);
if (!match.hasMatch())
break;
const AbstractMetaFunctionCPtr &func,
bool usePyArgs,
const OverloadData &overloadData,
+ const GeneratorContext &classContext,
ErrorReturn errorReturn)
{
const AbstractMetaArgumentList &args = OverloadData::getArgumentsWithDefaultValues(func);
s << "if (kwds != nullptr && PyDict_Size(kwds) > 0) {\n" << indent
<< "errInfo.reset(kwds);\n"
<< "Py_INCREF(errInfo.object());\n"
- << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
- << outdent << "}\n";
+ << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn)
+ << ";\n" << outdent << "}\n";
}
return;
}
<< "if (value != nullptr && " << pyArgName << " != nullptr ) {\n"
<< indent << "errInfo.reset(" << pyKeyName << ");\n"
<< "Py_INCREF(errInfo.object());\n"
- << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
- << outdent << "}\nif (value != nullptr) {\n" << indent
+ << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn)
+ << ";\n" << outdent << "}\nif (value != nullptr) {\n" << indent
<< pyArgName << " = value;\nif (!";
const auto &type = arg.modifiedType();
writeTypeCheck(s, type, pyArgName, isNumber(type.typeEntry()), {});
s << ")\n" << indent
- << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n"
- << outdent << outdent
+ << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn)
+ << ";\n" << outdent << outdent
<< "}\nPyDict_DelItem(kwds_dup, " << pyKeyName << ");\n"
<< outdent << "}\n";
}
// until extra keyword signals and properties are handled.
s << "if (PyDict_Size(kwds_dup) > 0) {\n" << indent
<< "errInfo.reset(kwds_dup.release());\n";
- if (!(func->isConstructor() && isQObject(func->ownerClass())))
- s << "return " << returnErrorWrongArguments(overloadData, errorReturn) << ";\n";
- else
+ if (!(func->isConstructor() && isQObject(func->ownerClass()))) {
+ s << "return " << returnErrorWrongArguments(overloadData, classContext, errorReturn)
+ << ";\n";
+ } else {
s << "// fall through to handle extra keyword signals and properties\n";
+ }
s << outdent << "}\n"
<< outdent << "}\n";
}
writeCustomConverterRegister(s, customConversion, converter);
}
-static void registerConverterInScopes(TextStream &s, QStringView signature,
- QAnyStringView varName = converterVar)
-{
- while (true) {
- s << registerConverterName(signature, varName);
- const auto qualifierPos = signature.indexOf("::"_L1);
- if (qualifierPos == -1)
- break;
- signature = signature.sliced(qualifierPos + 2);
- }
-}
-
void CppGenerator::writeEnumConverterInitialization(TextStream &s, const AbstractMetaEnum &metaEnum)
{
if (metaEnum.isPrivate() || metaEnum.isAnonymous())
const QString typeName = fixedCppTypeName(enumType);
s << "SbkConverter *converter = Shiboken::Conversions::createConverter("
<< enumPythonVar << ',' << '\n' << indent
- << cppToPythonFunctionName(typeName, typeName) << ");\n" << outdent;
+ << cppToPythonFunctionName(typeName, enumConverterPythonType) << ");\n" << outdent;
- const QString toCpp = pythonToCppFunctionName(typeName, typeName);
- const QString isConv = convertibleToCppFunctionName(typeName, typeName);
+ QString toCpp = pythonToCppFunctionName(enumConverterPythonType, typeName);
+ const QString isConv = convertibleToCppFunctionName(enumConverterPythonType, typeName);
writeAddPythonToCppConversion(s, u"converter"_s, toCpp, isConv);
- s << "Shiboken::Enum::setTypeConverter(" << enumPythonVar
- << ", converter);\n";
-
- registerConverterInScopes(s, enumType->qualifiedCppName());
- if (auto flags = enumType->flags())
+ s << registerConverterName(enumType->qualifiedCppName(), {},
+ registerConverterName::PartiallyQualifiedAliases);
+
+ QString flagsConverter = "nullptr"_L1;
+ if (auto flags = enumType->flags()) {
+ flagsConverter = "flagsConverter"_L1;
+ toCpp = pythonToCppFunctionName(flagsConverterPythonType, typeName);
+ s << "SbkConverter *" << flagsConverter
+ << " = Shiboken::Conversions::createConverter("
+ << enumPythonVar << ',' << '\n' << indent
+ << cppToPythonFunctionName(typeName, flagsConverterPythonType)
+ << ");\n" << outdent;
+ writeAddPythonToCppConversion(s, flagsConverter, toCpp, isConv);
s << "// Register converter for flag '" << flags->qualifiedCppName() << "'.\n"
- << registerConverterName(flags->name()) // QMetaType
- << registerConverterName(flags->originalName()); // Signals with flags
+ << registerConverterName(flags->name(), flagsConverter) // QMetaType
+ << registerConverterName(flags->originalName(), flagsConverter); // Signals with flags
+ }
+
+ s << "Shiboken::Enum::setTypeConverter(" << enumPythonVar
+ << ", converter, " << flagsConverter << ");\n";
s << outdent << "}\n";
}
return converter;
}
+QString CppGenerator::typeInitStructHelper(const TypeEntryCPtr &te, const QString &varName)
+{
+ return cppApiVariableName(te->targetLangPackage()) + u'[' + varName + u']';
+}
+
+QString CppGenerator::typeInitStruct(const GeneratorContext &context)
+{
+ Q_ASSERT(context.hasClass());
+ if (context.forSmartPointer()) {
+ auto te = context.preciseType().typeEntry();
+ return typeInitStructHelper(te, getTypeIndexVariableName(context.preciseType()));
+ }
+ return typeInitStruct(context.metaClass()->typeEntry());
+}
+
QString CppGenerator::typeInitStruct(const TypeEntryCPtr &te)
{
- return cppApiVariableName(te->targetLangPackage()) + u'['
- + getTypeIndexVariableName(te) + u']';
+ return typeInitStructHelper(te, getTypeIndexVariableName(te));
}
void CppGenerator::writeExtendedConverterInitialization(TextStream &s,
bool CppGenerator::supportsMappingProtocol(const AbstractMetaClassCPtr &metaClass)
{
- for (const auto &m : mappingProtocols()) {
- if (metaClass->hasFunction(m.name))
- return true;
- }
-
- return false;
+ const auto &protocols = mappingProtocols();
+ return std::any_of(protocols.cbegin(), protocols.cend(),
+ [&metaClass](const ProtocolEntry &e) {
+ return metaClass->hasFunction(e.name);
+ });
}
bool CppGenerator::supportsNumberProtocol(const AbstractMetaClassCPtr &metaClass)
tp_dealloc = isQApp
? u"&SbkDeallocQAppWrapper"_s : u"&SbkDeallocWrapper"_s;
if (!onlyPrivCtor && !ctors.isEmpty())
- tp_init = cpythonFunctionName(ctors.constFirst());
+ tp_init = cpythonConstructorName(metaClass);
}
const AttroCheck attroCheck = checkAttroFunctionNeeds(metaClass);
const AbstractMetaArgument *lastArg = func->arguments().isEmpty() ? nullptr : &func->arguments().constLast();
writeCodeSnips(s, snips,TypeSystem::CodeSnipPositionAny,
TypeSystem::TargetLangCode, func, false /* uses PyArgs */, lastArg);
- s<< outdent << "}\n\n";
+ s << outdent << "}\n\n";
}
if (!injectedCode)
const QList<AbstractMetaFunctionCList> opOverloads = numberProtocolOperators(metaClass);
for (const auto &opOverload : opOverloads) {
- const auto rfunc = opOverload.at(0);
+ const auto &rfunc = opOverload.at(0);
QString opName = ShibokenGenerator::pythonOperatorFunctionName(rfunc);
nb[opName] = cpythonFunctionName(rfunc);
}
const QString className = chopType(cpythonTypeName(metaClass));
const QString funcName = className + u"__copy__"_s;
- signatureStream << fullPythonClassName(metaClass) << ".__copy__()\n";
+ signatureStream << fullPythonClassName(metaClass) << ".__copy__(self)->typing.Self\n";
definitionStream << PyMethodDefEntry{u"__copy__"_s, funcName, {"METH_NOARGS"_ba}, {}}
<< ",\n";
<< sbkUnusedVariableCast(PYTHON_TO_CPP_VAR) << '\n';
}
-void CppGenerator::writeRichCompareFunction(TextStream &s,
+void CppGenerator::writeRichCompareFunction(TextStream &s, TextStream &t,
const GeneratorContext &context) const
{
const auto metaClass = context.metaClass();
bool first = true;
OverloadData overloadData(overloads, api());
+ writeSignatureInfo(t, overloadData);
const OverloadDataList &nextOverloads = overloadData.children();
for (const auto &od : nextOverloads) {
const auto func = od->referenceFunction();
}
// Format the type signature of a function parameter
-QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg) const
+QString CppGenerator::signatureParameter(const AbstractMetaArgument &arg, bool implicitConversions) const
{
QString result;
QTextStream s(&result);
// Implicit conversions (C++): Check for converting constructors
// "QColor(Qt::GlobalColor)" or conversion operators
- const AbstractMetaFunctionCList conversions =
- api().implicitConversions(metaType);
+ const AbstractMetaFunctionCList conversions = implicitConversions ?
+ api().implicitConversions(metaType) : AbstractMetaFunctionCList{};
for (const auto &f : conversions) {
if (f->isConstructor() && !f->arguments().isEmpty()) {
// PYSIDE-2712: modified types from converting constructors are not always correct
return result;
}
-void CppGenerator::writeSignatureInfo(TextStream &s, const OverloadData &overloadData) const
+void CppGenerator::writeSignatureInfo(TextStream &s, const OverloadData &overloadData,
+ bool useProperties) const
{
const auto rfunc = overloadData.referenceFunction();
QString funcName = fullPythonFunctionName(rfunc, false);
- int idx = overloadData.overloads().length() - 1;
+ auto idx = overloadData.overloads().length() - 1;
bool multiple = idx > 0;
for (const auto &f : overloadData.overloads()) {
// Toplevel functions like `PySide6.QtCore.QEnum` are always self-less.
if (!(f->isStatic()) && f->ownerClass())
args << PYTHON_SELF_VAR;
+ const bool implicitConversions = f->functionType() != AbstractMetaFunction::CopyConstructorFunction;
const auto &arguments = f->arguments();
for (qsizetype i = 0, size = arguments.size(); i < size; ++i) {
const auto n = i + 1;
if (!f->argumentRemoved(n)) {
QString t = f->pyiTypeReplaced(n);
if (t.isEmpty()) {
- t = signatureParameter(arg);
+ t = signatureParameter(arg, implicitConversions);
} else {
t.prepend(u':');
t.prepend(arg.name());
args.append(t);
}
}
+ // PYSIDE-1846: In a constructor, provide all properties as keyword-only parameters.
+ const auto &metaClass = rfunc->ownerClass();
+ if (useProperties && !metaClass->propertySpecs().isEmpty()) {
+ args << "*:KeywordOnly=None"_L1;
+ for (const auto &spec : metaClass->propertySpecs()) {
+ auto typeEntry = spec.typeEntry();
+ QString text;
+ if (typeEntry->isFlags()) {
+ const auto fte = std::static_pointer_cast<const FlagsTypeEntry>(typeEntry);
+ text = fte->originator()->qualifiedTargetLangName();
+ } else {
+ text = typeEntry->qualifiedCppName();
+ }
+ auto &inst = spec.type().instantiations();
+ if (!inst.isEmpty()) {
+ text += u'[';
+ for (qsizetype i = 0, size = inst.size(); i < size; ++i) {
+ if (i > 0)
+ text += u", "_s;
+ text += pythonSignature(inst.at(i));
+ }
+ text += u']';
+ }
+ QString entry = spec.name() + u':' + text.replace(u"::"_s, u"."_s) + "=None"_L1;
+ args.append(entry);
+ }
+ }
// mark the multiple signatures as such, to make it easier to generate different code
if (multiple)
QString enumVarTypeObj = cpythonTypeNameExtSet(enumTypeEntry);
if (!cppEnum.isAnonymous()) {
- int packageLevel = packageName().count(u'.') + 1;
+ auto packageLevel = packageName().count(u'.') + 1;
s << "EType = Shiboken::Enum::"
<< "createPythonEnum"
<< '(' << enclosingObjectVariable << ",\n" << indent
continue;
const AbstractMetaArgumentList &arguments = cppSignal->arguments();
for (const AbstractMetaArgument &arg : arguments) {
- AbstractMetaType metaType = arg.type();
+ const AbstractMetaType &metaType = arg.type();
const QByteArray origType =
QMetaObject::normalizedType(qPrintable(metaType.originalTypeDescription()));
const QByteArray cppSig =
return getSimpleClassInitFunctionName(context.metaClass());
}
+static QString formatHex(const QByteArray &data)
+{
+ QString result;
+ QTextStream str(&result);
+ const auto oldFieldWidth = str.fieldWidth();
+ str.setIntegerBase(16);
+ str.setPadChar(u'0');
+ for (qsizetype c = 0, size = data.size(); c < size; ++c) {
+ if (c != 0)
+ str << (c % 16 == 0 ? ",\n" : ", ");
+ str << "0x";
+ str.setFieldWidth(2);
+ str << uchar(data.at(c));
+ str.setFieldWidth(oldFieldWidth);
+ }
+ return result;
+}
+
+static constexpr int compressionLevel = 9; // almost no effect. Most blocks are small.
+
void CppGenerator::writeSignatureStrings(TextStream &s,
const QString &signatures,
const QString &arrayName,
{
s << "// The signatures string for the " << comment << ".\n"
<< "// Multiple signatures have their index \"n:\" in front.\n"
+ << "#if PYSIDE6_COMOPT_COMPRESS == 0\n"
<< "static const char *" << arrayName << "_SignatureStrings[] = {\n" << indent;
const auto lines = QStringView{signatures}.split(u'\n', Qt::SkipEmptyParts);
+
+ QByteArray buffer;
+ buffer.reserve(signatures.size());
for (auto line : lines) {
// must anything be escaped?
if (line.contains(u'"') || line.contains(u'\\'))
s << "R\"CPP(" << line << ")CPP\",\n";
else
s << '"' << line << "\",\n";
+ // collect also for compression
+ buffer += line.toLatin1();
+ buffer += '\n';
}
- s << NULL_PTR << "}; // Sentinel\n" << outdent << '\n';
+
+ // The Qt compressor has a 4-byte big endian length in front.
+ QByteArray compressed = qCompress(buffer, compressionLevel).sliced(4);
+ const auto binSize = compressed.size();
+ const auto adjustSize = qMax(binSize, 1); // Windows
+ if (compressed.isEmpty())
+ compressed.append('\0');
+
+ s << NULL_PTR << "}; // Sentinel\n" << outdent
+ << "#else\n"
+ << "static constexpr size_t " << arrayName << "_SignatureByteSize = " << binSize << ";\n"
+ << "static constexpr uint8_t " << arrayName << "_SignatureBytes[" << adjustSize << "] = {\n"
+ << indent << formatHex(compressed) << outdent << "\n};\n"
+ << "#endif\n\n";
}
// Return the class name for which to invoke the destructor
s << outdent << ");\nauto *pyType = " << pyTypeName << "; // references "
<< typePtr << "\n"
- << "InitSignatureStrings(pyType, " << initFunctionName << "_SignatureStrings);\n";
+ << outdent << "#if PYSIDE6_COMOPT_COMPRESS == 0\n" << indent
+ << "InitSignatureStrings(pyType, " << initFunctionName << "_SignatureStrings);\n"
+ << outdent << "#else\n" << indent
+ << "InitSignatureBytes(pyType, " << initFunctionName << "_SignatureBytes, "
+ << initFunctionName << "_SignatureByteSize);\n"
+ << outdent << "#endif\n" << indent;
if (usePySideExtensions() && !classContext.forSmartPointer())
s << "SbkObjectType_SetPropertyStrings(pyType, "
writeConverterRegister(s, metaClass, classContext);
s << '\n';
+ if (classContext.forSmartPointer())
+ writeSmartPointerConverterInitialization(s, classContext.preciseType());
+
// class inject-code target/beginning
if (!classTypeEntry->codeSnips().isEmpty()) {
writeClassCodeSnips(s, classTypeEntry->codeSnips(),
} else if (metaClass->isPolymorphic()) {
const auto &ancestors = metaClass->allTypeSystemAncestors();
for (const auto &ancestor : ancestors) {
- if (ancestor->baseClass())
+ if (ancestor->baseClass() && !ancestor->typeEntry()->isPolymorphicBase())
continue;
if (ancestor->isPolymorphic()) {
s << "if (instanceType == Shiboken::SbkType< " << m_gsp
// Write declaration and invocation of the init function for the module init
// function.
-void CppGenerator::writeInitFunc(TextStream &declStr, TextStream &callStr,
- const QString &initFunctionName,
- const TypeEntryCPtr &enclosingEntry,
- const QString &pythonName, bool lazy)
+static void writeInitFuncDecl(TextStream &declStr,
+ const QString &functionName)
{
- const QString functionName = "init_"_L1 + initFunctionName;
- const bool hasParent = enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType;
- declStr << "PyTypeObject *" << functionName << "(PyObject *"
- << (hasParent ? "enclosingClass" : "module") << ");\n";
+ declStr << "PyTypeObject *" << functionName << "(PyObject *enclosing);\n";
+}
+// Write declaration and invocation of the init function for the module init
+// function.
+void CppGenerator::writeInitFuncCall(TextStream &callStr,
+ const QString &functionName,
+ const TypeEntryCPtr &enclosingEntry,
+ const QString &pythonName, bool lazy)
+{
+ const bool hasParent = enclosingEntry && enclosingEntry->type() != TypeEntry::TypeSystemType;
if (!lazy) {
const QString enclosing = hasParent
? "reinterpret_cast<PyObject *>("_L1 + cpythonTypeNameExt(enclosingEntry) + u')'
} else if (hasParent) {
const QString &enclosingName = enclosingEntry->name();
const auto parts = QStringView{enclosingName}.split(u"::", Qt::SkipEmptyParts);
+ const QString namePathPrefix = enclosingEntry->name().replace("::"_L1, "."_L1);
callStr << "Shiboken::Module::AddTypeCreationFunction("
- << "module, \"" << pythonName << "\", " << functionName << ", \"";
- for (qsizetype i = 0; i < parts.size(); ++i) {
- if (i > 0)
- callStr << "\", \"";
- callStr << parts.at(i);
- }
- callStr << "\");\n";
+ << "module, \"" << parts[0] << "\", "
+ << functionName << ", \"" << namePathPrefix << '.' << pythonName << "\");\n";
} else {
callStr << "Shiboken::Module::AddTypeCreationFunction("
<< "module, \"" << pythonName << "\", "
- << "init_" << initFunctionName << ");\n";
+ << functionName << ");\n";
}
}
s_classInitDecl << te->configCondition() << '\n';
s_classPythonDefines << te->configCondition() << '\n';
}
- writeInitFunc(s_classInitDecl, s_classPythonDefines,
- getSimpleClassInitFunctionName(cls),
- targetLangEnclosingEntry(te), cls->name());
+ const QString initFunc = initFuncPrefix + getSimpleClassInitFunctionName(cls);
+ writeInitFuncDecl(s_classInitDecl, initFunc);
+ writeInitFuncCall(s_classPythonDefines, initFunc,
+ targetLangEnclosingEntry(te), cls->name());
if (cls->hasStaticFields()) {
s_classInitDecl << "PyTypeObject *"
<< getSimpleClassStaticFieldsInitFunctionName(cls) << "(PyObject *module);\n";
const auto enclosingClass = context.metaClass()->enclosingClass();
auto enclosingTypeEntry = targetLangEnclosingEntry(smp.specialized->typeEntry());
- writeInitFunc(s_classInitDecl, s_classPythonDefines,
- getInitFunctionName(context),
- enclosingTypeEntry, smp.type.name());
+ const QString initFunc = initFuncPrefix + getInitFunctionName(context);
+ writeInitFuncDecl(s_classInitDecl, initFunc);
+ writeInitFuncCall(s_classPythonDefines,
+ initFunc, enclosingTypeEntry, smp.specialized->name());
includes.insert(smp.type.instantiations().constFirst().typeEntry()->include());
}
- for (auto &instantiatedContainer : api().instantiatedContainers()) {
+ for (const auto &instantiatedContainer : api().instantiatedContainers()) {
includes.insert(instantiatedContainer.typeEntry()->include());
for (const auto &inst : instantiatedContainer.instantiations())
includes.insert(inst.typeEntry()->include());
const ExtendedConverterData extendedConverters = getExtendedConverters();
for (auto it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) {
- TypeEntryCPtr te = it.key();
+ const TypeEntryCPtr &te = it.key();
includes.insert(te->include());
for (const auto &metaClass : it.value())
includes.insert(metaClass->typeEntry()->include());
if (!api().instantiatedContainers().isEmpty())
s << "#include <sbkcontainer.h>\n#include <sbkstaticstrings.h>\n";
+ s << '\n' << compilerOptionOptimize() << '\n';
if (usePySideExtensions()) {
s << includeQDebug;
s << R"(#include <pysidecleanup.h>
if (!extendedConverters.isEmpty()) {
s << '\n' << "// Extended Converters.\n\n";
for (ExtendedConverterData::const_iterator it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) {
- TypeEntryCPtr externalType = it.key();
+ const TypeEntryCPtr &externalType = it.key();
s << "// Extended implicit conversions for "
<< externalType->qualifiedTargetLangName() << '.' << '\n';
for (const auto &sourceClass : it.value()) {
s << '\n';
}
- // Implicit smart pointers conversions
- const auto &smartPointersList = api().instantiatedSmartPointers();
- if (!smartPointersList.isEmpty()) {
- s << "// SmartPointers converters.\n\n";
- for (const auto &smp : smartPointersList) {
- s << "// C++ to Python conversion for smart pointer type '"
- << smp.type.cppSignature() << "'.\n";
- writeSmartPointerConverterFunctions(s, smp.type);
- }
- s << '\n';
- }
-
s << "static struct PyModuleDef moduledef = {\n"
<< " /* m_base */ PyModuleDef_HEAD_INIT,\n"
<< " /* m_name */ \"" << moduleName() << "\",\n"
collectFullTypeNamesArray(typeNames);
- for (auto typeName : typeNames)
+ for (const auto &typeName : typeNames)
s << "{nullptr, \"" << typeName << "\"},\n";
s << "{nullptr, nullptr}\n" << outdent << "};\n"
if (!opaqueContainers.isEmpty()) {
s << "\n// Opaque container type registration\n"
<< "PyObject *ob_type{};\n";
+ if (usePySideExtensions()) {
+ const bool hasQVariantConversion =
+ std::any_of(opaqueContainers.cbegin(), opaqueContainers.cend(),
+ [](const OpaqueContainerData &d) { return d.hasQVariantConversion; });
+ if (hasQVariantConversion) {
+ const char qVariantConverterVar[] = "qVariantConverter";
+ s << "auto *" << qVariantConverterVar
+ << " = Shiboken::Conversions::getConverter(\"QVariant\");\n"
+ << "Q_ASSERT(" << qVariantConverterVar << " != nullptr);\n";
+ }
+ }
for (const auto &d : opaqueContainers)
s << d.registrationCode;
s << '\n';
}
- if (!smartPointersList.isEmpty()) {
- s << '\n';
- for (const auto &smp : smartPointersList) {
- writeSmartPointerConverterInitialization(s, smp.type);
- s << '\n';
- }
- }
-
if (!extendedConverters.isEmpty()) {
s << '\n';
for (ExtendedConverterData::const_iterator it = extendedConverters.cbegin(), end = extendedConverters.cend(); it != end; ++it) {
if (!pte->referencesType())
continue;
TypeEntryCPtr referencedType = basicReferencedTypeEntry(pte);
- registerConverterInScopes(s, pte->qualifiedCppName(), converterObject(referencedType));
+ s << registerConverterName(pte->qualifiedCppName(), converterObject(referencedType),
+ registerConverterName::Alias
+ | registerConverterName::PartiallyQualifiedAliases);
}
s << '\n';
}
// finish the rest of get_signature() initialization.
- s << "FinishSignatureInitialization(module, " << moduleName()
- << "_SignatureStrings);\n"
+ s << outdent << "#if PYSIDE6_COMOPT_COMPRESS == 0\n" << indent
+ << "FinishSignatureInitialization(module, " << moduleName() << "_SignatureStrings);\n"
+ << outdent << "#else\n" << indent
+ << "if (FinishSignatureInitBytes(module, " << moduleName() << "_SignatureBytes, "
+ << moduleName() << "_SignatureByteSize) < 0)\n" << indent << "return {};\n" << outdent
+ << outdent << "#endif\n" << indent
<< "\nreturn module;\n" << outdent << "}\n";
file.done();
}
}
-void CppGenerator::writeHashFunction(TextStream &s, const GeneratorContext &context)
+void CppGenerator::writeHashFunction(TextStream &s, TextStream &t, const GeneratorContext &context)
{
const auto metaClass = context.metaClass();
+ t << fullPythonClassName(metaClass) << ".__hash__(self)->int\n";
const char hashType[] = "Py_hash_t";
s << "static " << hashType << ' ' << cpythonBaseName(metaClass)
<< "_HashFunc(PyObject *self)\n{\n" << indent;
s << "PyObject *" << namePrefix
<< "__getitem__(PyObject *self, Py_ssize_t _i)\n{\n" << indent;
writeCppSelfDefinition(s, context, errorReturn);
- writeIndexError(s, u"index out of bounds"_s, errorReturn);
+ writeIndexError(s, ErrorReturn::NullPtr);
s << metaClass->qualifiedCppName() << "::const_iterator _item = "
<< CPP_SELF_VAR << "->begin();\n"
<< indent;
errorReturn = ErrorReturn::MinusOne;
writeCppSelfDefinition(s, context, errorReturn);
- writeIndexError(s, u"list assignment index out of range"_s, errorReturn);
+ writeIndexError(s, errorReturn);
s << PYTHON_TO_CPPCONVERSION_STRUCT << ' ' << PYTHON_TO_CPP_VAR << ";\n"
<< "if (!";
s << "return {};\n" << outdent << "}\n";
}
-void CppGenerator::writeIndexError(TextStream &s, const QString &errorMsg,
- ErrorReturn errorReturn)
+void CppGenerator::writeIndexError(TextStream &s, ErrorReturn errorReturn)
{
- s << "if (_i < 0 || _i >= (Py_ssize_t) " << CPP_SELF_VAR << "->size()) {\n"
- << indent << "PyErr_SetString(PyExc_IndexError, \"" << errorMsg << "\");\n"
+ s << "const Py_ssize_t size = " << CPP_SELF_VAR << "->size();\n"
+ << "if (_i < 0 || _i >= size) {\n"
+ << indent << "Shiboken::Errors::setIndexOutOfBounds(_i, 0, size);\n"
<< errorReturn << outdent << "}\n";
}
Default, // "{}"
Zero,
MinusOne,
+ NullPtr,
Void
};
protected:
QString fileNameForContext(const GeneratorContext &context) const override;
- void generateClass(TextStream &s, const GeneratorContext &classContext) override;
+ void generateClass(TextStream &s, const QString &targetDir,
+ const GeneratorContext &classContext,
+ QList<GeneratorContext> *contexts) override;
+ void generateSmartPointerClass(TextStream &s, const QString &targetDir,
+ const GeneratorContext &classContext) override;
bool finishGeneration() override;
private:
bool needsReference = false;
};
-
- void generateSmartPointerClass(TextStream &s, const GeneratorContext &classContext);
void generateIncludes(TextStream &s, const GeneratorContext &classContext,
const IncludeGroupList &includes = {},
const AbstractMetaClassCList &innerClasses = {}) const;
- static void writeInitFunc(TextStream &declStr, TextStream &callStr,
- const QString &initFunctionName,
- const TypeEntryCPtr &enclosingEntry,
- const QString &pythonName, bool lazy = true);
+ static void writeInitFuncCall(TextStream &callStr,
+ const QString &functionName,
+ const TypeEntryCPtr &enclosingEntry,
+ const QString &pythonName, bool lazy = true);
static void writeCacheResetNative(TextStream &s, const GeneratorContext &classContext);
void writeConstructorNative(TextStream &s, const GeneratorContext &classContext,
const AbstractMetaFunctionCPtr &func) const;
QString converterCheckFunctionName;
QString pythonToConverterFunctionName;
QString registrationCode;
+ bool hasQVariantConversion = false;
};
OpaqueContainerData
const AbstractMetaFunctionCList &overloads,
const GeneratorContext &classContext) const;
static void writeArgumentsInitializer(TextStream &s, const OverloadData &overloadData,
+ const GeneratorContext &classContext,
ErrorReturn errorReturn = ErrorReturn::Default);
static void writeCppSelfConversion(TextStream &s,
const GeneratorContext &context,
ErrorReturn errorReturn);
static QString returnErrorWrongArguments(const OverloadData &overloadData,
+ const GeneratorContext &context,
ErrorReturn errorReturn);
static void writeFunctionReturnErrorCheckSection(TextStream &s,
* \param overloadData the overload data describing all the possible overloads for the function/method
*/
void writeOverloadedFunctionDecisor(TextStream &s, const OverloadData &overloadData,
+ const GeneratorContext &classContext,
ErrorReturn errorReturn) const;
/// Recursive auxiliar method to the other writeOverloadedFunctionDecisor.
void writeOverloadedFunctionDecisorEngine(TextStream &s,
const AbstractMetaFunctionCPtr &func,
bool usePyArgs,
const OverloadData &overloadData,
+ const GeneratorContext &classContext,
ErrorReturn errorReturn);
/// Returns a string containing the name of an argument for the given function and argument index.
QByteArrayList methodDefinitionParameters(const OverloadData &overloadData) const;
QList<PyMethodDefEntry> methodDefinitionEntries(const OverloadData &overloadData) const;
- void writeSignatureInfo(TextStream &s, const OverloadData &overloads) const;
- QString signatureParameter(const AbstractMetaArgument &arg) const;
+ void writeSignatureInfo(TextStream &s, const OverloadData &overloads,
+ bool useProperties=false) const;
+ QString signatureParameter(const AbstractMetaArgument &arg, bool implicitConversions) const;
QString pythonSignature(const AbstractMetaType &type) const;
/// Writes the implementation of all methods part of python sequence protocol
void writeSequenceMethods(TextStream &s,
static void writeRichCompareFunctionHeader(TextStream &s,
const QString &baseName,
const GeneratorContext &context);
- void writeRichCompareFunction(TextStream &s, const GeneratorContext &context) const;
+ void writeRichCompareFunction(TextStream &s, TextStream &t, const GeneratorContext &context) const;
void writeSmartPointerRichCompareFunction(TextStream &s, const GeneratorContext &context) const;
static void writeEnumsInitialization(TextStream &s, AbstractMetaEnumList &enums);
void writeSmartPointerConverterInitialization(TextStream &s, const AbstractMetaType &ype) const;
static QString typeInitStruct(const TypeEntryCPtr &te);
+ static QString typeInitStruct(const GeneratorContext &context);
static void writeExtendedConverterInitialization(TextStream &s,
const TypeEntryCPtr &externalType,
const AbstractMetaClassCList &conversions);
static bool shouldGenerateGetSetList(const AbstractMetaClassCPtr &metaClass);
static bool hasHashFunction(const AbstractMetaClassCPtr &c);
- static void writeHashFunction(TextStream &s, const GeneratorContext &context);
+ static void writeHashFunction(TextStream &s, TextStream &t, const GeneratorContext &context);
/// Write default implementations for sequence protocol
void writeDefaultSequenceMethods(TextStream &s, const GeneratorContext &context) const;
/// Helper function for writeStdListWrapperMethods.
- static void writeIndexError(TextStream &s, const QString &errorMsg,
- ErrorReturn errorReturn);
+ static void writeIndexError(TextStream &s, ErrorReturn errorReturn);
static QString writeReprFunctionHeader(TextStream &s, const GeneratorContext &context);
static QString writeReprFunction(TextStream &s,
void clearTpFuncs();
static QString chopType(QString s);
+ static QString typeInitStructHelper(const TypeEntryCPtr &te, const QString &varName);
+
QHash<QString, QString> m_tpFuncs;
QHash<QString, QString> m_nbFuncs;
};
<< "}\n\n";
}
+static void writeConverterCheckFunc(TextStream &s,
+ const QString &name,
+ const QString &checkFunction,
+ const QString &converterFunction)
+{
+ s << "extern \"C\" PythonToCppFunc " << name << "(PyObject *" << PYTHON_ARG << ")\n{\n"
+ << indent << "if (" << checkFunction << '(' << PYTHON_ARG << "))\n" << indent
+ << "return " << converterFunction << ";\n"
+ << outdent << "return {};\n" << outdent << "}\n\n";
+}
+
+static void writeConverterFunctionHeader(TextStream &s,
+ const QString &name,
+ const QString &typeName)
+{
+ s << "extern \"C\" void " << name << "(PyObject *" << PYTHON_ARG << ", void *cppOut)\n{\n"
+ << indent << "auto *d = ShibokenSequenceContainerPrivate<" << typeName
+ << ">::get(" << PYTHON_ARG << ");\n";
+}
+
+static inline void writeConverterFunctionFooter(TextStream &s)
+{
+ s << outdent << "}\n\n";
+}
+
// Generate template specialization of value converter helper
void CppGenerator::writeOpaqueContainerValueConverter(TextStream &s,
const AbstractMetaType &valueType) const
// methods
const QString &containerName = containerType.name();
const bool isStdVector = containerName == u"std::vector";
+ result.hasQVariantConversion = usePySideExtensions() && containerName == "QList"_L1;
const auto kind = containerTypeEntry->containerKind();
const bool isFixed = kind == ContainerTypeEntry::SpanContainer || containerName == u"std::array";
const QString methods = result.name + u"_methods"_s;
// type creation function that sets a key in the type dict.
const QString typeCreationFName = u"create"_s + result.name + u"Type"_s;
s << "static inline PyTypeObject *" << typeCreationFName << "()\n{\n" << indent
- << "auto *result = reinterpret_cast<PyTypeObject *>(SbkType_FromSpec(&"
- << specName << "));\nPy_INCREF(Py_True);\n"
+ << "auto *result = SbkType_FromSpec(&"
+ << specName << ");\nPy_INCREF(Py_True);\n"
<< "Shiboken::AutoDecRef tpDict(PepType_GetDict(result));\n"
<< "PyDict_SetItem(tpDict.object(), "
"Shiboken::PyMagicName::opaque_container(), Py_True);\n"
// SBK converter Python to C++
result.pythonToConverterFunctionName = u"PythonToCpp"_s + result.name;
- s << "extern \"C\" void " << result.pythonToConverterFunctionName
- << "(PyObject *" << PYTHON_ARG << ", void *cppOut)\n{\n" << indent
- << "auto *d = ShibokenSequenceContainerPrivate<" << cppSignature
- << ">::get(" << PYTHON_ARG << ");\n"
- << "*reinterpret_cast<" << cppSignature << "**>(cppOut) = d->m_list;\n"
- << outdent << "}\n\n";
+ writeConverterFunctionHeader(s, result.pythonToConverterFunctionName, cppSignature);
+ s << "*reinterpret_cast<" << cppSignature << "**>(cppOut) = d->m_list;\n";
+ writeConverterFunctionFooter(s);
// SBK check function for converting Python to C++ that returns the converter
result.converterCheckFunctionName = u"is"_s + result.name + u"PythonToCppConvertible"_s;
- s << "extern \"C\" PythonToCppFunc " << result.converterCheckFunctionName
- << "(PyObject *" << PYTHON_ARG << ")\n{\n" << indent << "if ("
- << result.checkFunctionName << '(' << PYTHON_ARG << "))\n" << indent
- << "return " << result.pythonToConverterFunctionName << ";\n"
- << outdent << "return {};\n" << outdent << "}\n\n";
-
- QTextStream(&result.registrationCode) << "ob_type = reinterpret_cast<PyObject *>("
- << typeFName
- << "());\nPy_XINCREF(ob_type);\nPyModule_AddObject(module, \""
+ writeConverterCheckFunc(s, result.converterCheckFunctionName, result.checkFunctionName,
+ result.pythonToConverterFunctionName);
+
+ TextStream registrationStr(&result.registrationCode, TextStream::Language::Cpp);
+ registrationStr << "ob_type = reinterpret_cast<PyObject *>("
+ << typeFName << "());\nPy_XINCREF(ob_type);\nPyModule_AddObject(module, \""
<< result.name << "\", ob_type);\n";
+
+ if (!result.hasQVariantConversion)
+ return result;
+
+ // PYSIDE-2820: Add a conversion to QVariant for QML. Note QVariant::fromValue()
+ // will work for all list types, but we restrict it to registered meta types for QML.
+ QString pythonToQVariantConverterFunctionName = "PythonToQVariant"_L1 + result.name;
+ QString pythonToQVariantCheckFunctionName = "is"_L1 + result.name +
+ "PythonToQVariantConvertible"_L1;
+ writeConverterFunctionHeader(s, pythonToQVariantConverterFunctionName, cppSignature);
+ s << "*reinterpret_cast<QVariant *>(cppOut) = QVariant::fromValue(*d->m_list);\n";
+ writeConverterFunctionFooter(s);
+ writeConverterCheckFunc(s, pythonToQVariantCheckFunctionName,
+ result.checkFunctionName,
+ pythonToQVariantConverterFunctionName);
+ registrationStr << "if constexpr (QMetaTypeId2<" << valueType.cppSignature()
+ << ">::Defined) {\n" << indent
+ << "Shiboken::Conversions::prependPythonToCppValueConversion(qVariantConverter,\n "
+ << pythonToQVariantConverterFunctionName << ", "
+ << pythonToQVariantCheckFunctionName << ");\n" << outdent << "}\n";
+
return result;
}
return !f->arguments().isEmpty();
}
-void CppGenerator::generateSmartPointerClass(TextStream &s, const GeneratorContext &classContext)
+void CppGenerator::generateSmartPointerClass(TextStream &s,
+ const QString & /* targetDir */,
+ const GeneratorContext &classContext)
{
s.setLanguage(TextStream::Language::Cpp);
AbstractMetaClassCPtr metaClass = classContext.metaClass();
writeCopyFunction(s, md, signatureStream, classContext);
writeSmartPointerDirFunction(s, md, signatureStream, classContext);
- const QString methodsDefinitions = md.toString();
- const QString singleMethodDefinitions = smd.toString();
+ const QString &methodsDefinitions = md.toString();
+ const QString &singleMethodDefinitions = smd.toString();
const QString className = chopType(cpythonTypeName(typeEntry));
s << closeExternC;
if (hasHashFunction(metaClass))
- writeHashFunction(s, classContext);
+ writeHashFunction(s, signatureStream, classContext);
// Write tp_traverse and tp_clear functions.
writeTpTraverseFunction(s, metaClass);
s << '\n';
writeConverterFunctions(s, metaClass, classContext);
+ // Implicit smart pointers conversions
+ writeSmartPointerConverterFunctions(s, classContext.preciseType());
writeClassRegister(s, metaClass, classContext, signatureStream);
// class inject-code native/end
}
}
-void HeaderGenerator::generateClass(TextStream &s, const GeneratorContext &classContext)
+void HeaderGenerator::generateClass(TextStream &s, const QString &,
+ const GeneratorContext &classContext,
+ QList<GeneratorContext> *)
+{
+ doGenerateClass(s, classContext);
+}
+
+void HeaderGenerator::generateSmartPointerClass(TextStream &s,
+ const QString &,
+ const GeneratorContext &classContext)
+{
+ doGenerateClass(s, classContext);
+}
+
+void HeaderGenerator::doGenerateClass(TextStream &s, const GeneratorContext &classContext) const
{
const AbstractMetaClassCPtr metaClass = classContext.metaClass();
// Includes
s << metaClass->typeEntry()->include() << '\n';
- for (auto &inst : metaClass->templateBaseClassInstantiations())
+ for (const auto &inst : metaClass->templateBaseClassInstantiations())
s << inst.typeEntry()->include();
if (classContext.useWrapper())
const auto typeEntry = metaClass->typeEntry();
InheritedOverloadSet inheritedOverloads;
- // write license comment
- s << licenseComment();
-
// Class
s << "class " << wrapperName
<< " : public " << metaClass->qualifiedCppName()
if (generation.testFlag(FunctionGenerationFlag::VirtualMethod))
maxOverrides++;
}
- if (!maxOverrides)
- maxOverrides = 1;
//destructor
// PYSIDE-504: When C++ 11 is used, then the destructor must always be declared.
if (usePySideExtensions())
s << "static void pysideInitQtMetaTypes();\n";
- s << "void resetPyMethodCache();\n"
- << outdent << "private:\n" << indent;
+ const bool needsMethodCache = useOverrideCaching(metaClass);
+ Q_ASSERT(maxOverrides > 0 || !needsMethodCache);
+
+ if (needsMethodCache)
+ s << "void resetPyMethodCache();\n";
+
+ s << outdent << "private:\n" << indent;
if (!metaClass->userAddedPythonOverrides().isEmpty()) {
for (const auto &f : metaClass->userAddedPythonOverrides())
s << '\n';
}
- s << "mutable bool m_PyMethodCache[" << maxOverrides << "];\n"
- << outdent << "};\n\n";
+ if (needsMethodCache) {
+ s << "mutable bool m_PyMethodCache[" << maxOverrides << "] = {false";
+ for (int i = 1; i < maxOverrides; ++i)
+ s << ", false";
+ s << "};\n";
+ }
+
+ s << outdent << "};\n\n";
}
// Write an inline wrapper around a function
const QString &postfix) const
{
Q_ASSERT(!func->isConstructor() && !func->isOperatorOverload());
- s << "inline ";
s << functionSignature(func, {}, postfix, Generator::OriginalTypeDescription)
<< " { ";
if (!func->isVoid())
static void writeNamespaceForwardDeclarationRecursion(TextStream &s, qsizetype idx,
const NameSpaces &nameSpaces)
{
- auto &root = nameSpaces.at(idx);
+ const auto &root = nameSpaces.at(idx);
s << '\n';
if (root.nameSpace->isInlineNamespace())
s << "inline ";
void HeaderGenerator::writeProtectedEnumSurrogate(TextStream &s, const AbstractMetaEnum &cppEnum)
{
- if (avoidProtectedHack() && cppEnum.isProtected())
- s << "enum " << protectedEnumSurrogateName(cppEnum) << " {};\n";
+ if (avoidProtectedHack() && cppEnum.isProtected()) {
+ s << "enum " << protectedEnumSurrogateName(cppEnum) << " : " <<
+ cppEnum.underlyingType() << " {};\n";
+ }
}
void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaEnum &cppEnum)
protected:
QString fileNameForContext(const GeneratorContext &context) const override;
- void generateClass(TextStream &s, const GeneratorContext &classContext) override;
+ void generateClass(TextStream &s, const QString &targetDir,
+ const GeneratorContext &classContext,
+ QList<GeneratorContext> *contexts) override;
+ void generateSmartPointerClass(TextStream &s,
+ const QString &targetDir,
+ const GeneratorContext &classContext) override;
bool finishGeneration() override;
private:
using InheritedOverloadSet = QSet<AbstractMetaFunctionCPtr>;
using IndexValues = QList<IndexValue>;
+ void doGenerateClass(TextStream &s, const GeneratorContext &classContext) const;
IndexValues collectTypeIndexes(const AbstractMetaClassCList &classList);
IndexValues collectConverterIndexes() const;
};
#endif // HEADERGENERATOR_H
-
OverloadDataRootNode *parent,
const AbstractMetaArgument &argument,
int argPos,
- const QString argTypeReplaced) :
+ const QString &argTypeReplaced) :
m_argument(argument),
m_argTypeReplaced(argTypeReplaced),
m_parent(parent),
return overloadData.get();
}
+static bool isNonVoidOverload(const AbstractMetaFunctionCPtr &func)
+{
+ return func->isTypeModified()
+ ? func->modifiedTypeName() != "void"_L1
+ : !func->argumentRemoved(0) && !func->type().isVoid();
+}
+
bool OverloadData::hasNonVoidReturnType() const
{
- for (const auto &func : m_overloads) {
- if (func->isTypeModified()) {
- if (func->modifiedTypeName() != u"void")
- return true;
- } else {
- if (!func->argumentRemoved(0) && !func->type().isVoid())
- return true;
- }
- }
- return false;
+ return std::any_of(m_overloads.cbegin(), m_overloads.cend(),
+ isNonVoidOverload);
+}
+
+static bool functionHasVarargs(const AbstractMetaFunctionCPtr &func)
+{
+ const auto &args = func->arguments();
+ return args.size() > 1 && args.constLast().type().isVarargs();
}
bool OverloadData::hasVarargs() const
{
- for (const auto &func : m_overloads) {
- AbstractMetaArgumentList args = func->arguments();
- if (args.size() > 1 && args.constLast().type().isVarargs())
- return true;
- }
- return false;
+ return std::any_of(m_overloads.cbegin(), m_overloads.cend(),
+ functionHasVarargs);
+}
+
+static bool isStaticFunction(const AbstractMetaFunctionCPtr &func)
+{
+ return func->isStatic();
}
bool OverloadData::hasStaticFunction(const AbstractMetaFunctionCList &overloads)
{
- for (const auto &func : overloads) {
- if (func->isStatic())
- return true;
- }
- return false;
+ return std::any_of(overloads.cbegin(), overloads.cend(), isStaticFunction);
}
bool OverloadData::hasStaticFunction() const
{
- for (const auto &func : m_overloads) {
- if (func->isStatic())
- return true;
- }
- return false;
+ return std::any_of(m_overloads.cbegin(), m_overloads.cend(), isStaticFunction);
+}
+
+static bool isClassMethod(const AbstractMetaFunctionCPtr &func)
+{
+ return func->isClassMethod();
}
bool OverloadData::hasClassMethod(const AbstractMetaFunctionCList &overloads)
{
- for (const auto &func : overloads) {
- if (func->isClassMethod())
- return true;
- }
- return false;
+ return std::any_of(overloads.cbegin(), overloads.cend(), isClassMethod);
}
bool OverloadData::hasClassMethod() const
{
- for (const auto &func : m_overloads) {
- if (func->isClassMethod())
- return true;
- }
- return false;
+ return std::any_of(m_overloads.cbegin(), m_overloads.cend(), isClassMethod);
}
bool OverloadData::hasInstanceFunction(const AbstractMetaFunctionCList &overloads)
{
- for (const auto &func : overloads) {
- if (!func->isStatic())
- return true;
- }
- return false;
+ return !std::all_of(overloads.cbegin(), overloads.cend(), isStaticFunction);
}
bool OverloadData::hasInstanceFunction() const
{
- for (const auto &func : m_overloads) {
- if (!func->isStatic())
- return true;
- }
- return false;
+ return !std::all_of(m_overloads.cbegin(), m_overloads.cend(), isStaticFunction);
}
bool OverloadData::hasStaticAndInstanceFunctions(const AbstractMetaFunctionCList &overloads)
return &func->arguments().at(m_argPos + removed);
}
+static bool hasDefaultValue(const OverloadDataNodePtr &o)
+{
+ return o->getFunctionWithDefaultValue() != nullptr;
+}
+
bool OverloadDataRootNode::nextArgumentHasDefaultValue() const
{
- for (const auto &overloadData : m_children) {
- if (overloadData->getFunctionWithDefaultValue())
- return true;
- }
- return false;
+ return std::any_of(m_children.cbegin(), m_children.cend(), hasDefaultValue);
}
static const OverloadDataRootNode *_findNextArgWithDefault(const OverloadDataRootNode *overloadData)
bool OverloadDataRootNode::isFinalOccurrence(const AbstractMetaFunctionCPtr &func) const
{
- for (const auto &pd : m_children) {
- if (pd->overloads().contains(func))
- return false;
- }
- return true;
+ auto containsPredicate = [&func](const OverloadDataNodePtr &o) {
+ return o->overloads().contains(func);
+ };
+ return std::none_of(m_children.cbegin(), m_children.cend(), containsPredicate);
}
AbstractMetaFunctionCPtr OverloadDataRootNode::getFunctionWithDefaultValue() const
bool OverloadData::hasArgumentWithDefaultValue() const
{
- if (maxArgs() == 0)
- return false;
- for (const auto &func : m_overloads) {
- if (hasArgumentWithDefaultValue(func))
- return true;
- }
- return false;
+ return maxArgs() != 0
+ && std::any_of(m_overloads.cbegin(), m_overloads.cend(),
+ qOverload<const AbstractMetaFunctionCPtr &>(hasArgumentWithDefaultValue));
+}
+
+static bool isArgumentWithDefaultValue(const AbstractMetaArgument &arg)
+{
+ return !arg.isModifiedRemoved() && arg.hasDefaultValueExpression();
}
bool OverloadData::hasArgumentWithDefaultValue(const AbstractMetaFunctionCPtr &func)
{
const AbstractMetaArgumentList &arguments = func->arguments();
- for (const AbstractMetaArgument &arg : arguments) {
- if (!arg.isModifiedRemoved() && arg.hasDefaultValueExpression())
- return true;
- }
- return false;
+ return std::any_of(arguments.cbegin(), arguments.cend(), isArgumentWithDefaultValue);
}
AbstractMetaArgumentList OverloadData::getArgumentsWithDefaultValues(const AbstractMetaFunctionCPtr &func)
explicit OverloadDataNode(const AbstractMetaFunctionCPtr &func,
OverloadDataRootNode *parent,
const AbstractMetaArgument &arg, int argPos,
- const QString argTypeReplaced = {});
+ const QString &argTypeReplaced = {});
void addOverload(const AbstractMetaFunctionCPtr &func);
int argPos() const override { return m_argPos; }
ShibokenGenerator::FunctionGroups functionGroups;
QList<AbstractMetaFunctionCList> numberProtocolOperators;
BoolCastFunctionOptional boolCastFunctionO;
- bool needsGetattroFunction = false;
+ ShibokenGenerator::AttroCheck attroCheck;
};
using GeneratorClassInfoCache = QHash<AbstractMetaClassCPtr, GeneratorClassInfoCacheEntry>;
return result + u"_Surrogate"_s;
}
+QString ShibokenGenerator::cpythonConstructorName(const AbstractMetaClassCPtr &metaClass)
+{
+ return cpythonBaseName(metaClass->typeEntry()) + u"_Init"_s;
+}
+
QString ShibokenGenerator::cpythonFunctionName(const AbstractMetaFunctionCPtr &func)
{
- QString result;
+ if (func->isConstructor())
+ return cpythonConstructorName(func->implementingClass());
+ QString result;
// PYSIDE-331: For inherited functions, we need to find the same labels.
// Therefore we use the implementing class.
if (func->implementingClass()) {
- result = cpythonBaseName(func->implementingClass()->typeEntry());
- if (func->isConstructor()) {
- result += u"_Init"_s;
- } else {
- result += u"Func_"_s;
- if (func->isOperatorOverload())
- result += ShibokenGenerator::pythonOperatorFunctionName(func);
- else
- result += func->name();
- }
- } else {
- result = u"Sbk"_s + moduleName() + u"Module_"_s + func->name();
+ return cpythonBaseName(func->implementingClass()->typeEntry())
+ + u"Func_"_s
+ + (func->isOperatorOverload()
+ ? ShibokenGenerator::pythonOperatorFunctionName(func)
+ : func->name());
}
- return result;
+ return u"Sbk"_s + moduleName() + u"Module_"_s + func->name();
}
QString ShibokenGenerator::cpythonMethodDefinitionName(const AbstractMetaFunctionCPtr &func)
const QString &argName)
{
if (!type->isWrapperType())
- return QString();
+ return {};
return u"reinterpret_cast< "_s + getFullTypeName(type)
+ u" *>(Shiboken::Conversions::cppPointer("_s + cpythonTypeNameExt(type)
+ u", reinterpret_cast<SbkObject *>("_s + argName + u")))"_s;
return converterObject(typeEntry);
}
+static QString sbkEnumPrivate(const QString &name)
+{
+ return "PepType_SETP(reinterpret_cast<SbkEnumType *>("_L1 + name + "))"_L1;
+}
+
QString ShibokenGenerator::converterObject(const TypeEntryCPtr &type)
{
- if (isExtendedCppPrimitive(type))
- return QString::fromLatin1("Shiboken::Conversions::PrimitiveTypeConverter<%1>()")
- .arg(type->qualifiedCppName());
- if (type->isWrapperType())
- return QString::fromLatin1("PepType_SOTP(reinterpret_cast<PyTypeObject *>(%1))->converter")
- .arg(cpythonTypeNameExt(type));
- if (type->isEnum() || type->isFlags())
- return QString::fromLatin1("PepType_SETP(reinterpret_cast<SbkEnumType *>(%1))->converter")
- .arg(cpythonTypeNameExt(type));
+ if (isExtendedCppPrimitive(type)) {
+ return "Shiboken::Conversions::PrimitiveTypeConverter<"_L1
+ + type->qualifiedCppName() + ">()"_L1;
+ }
+
+ if (type->isWrapperType()) {
+ return "PepType_SOTP(reinterpret_cast<PyTypeObject *>("_L1
+ + cpythonTypeNameExt(type) + "))->converter"_L1;
+ }
+
+ if (type->isEnum())
+ return sbkEnumPrivate(cpythonTypeNameExt(type)) + "->converter"_L1;
+ if (type->isFlags())
+ return sbkEnumPrivate(cpythonTypeNameExt(type)) + "->flagsConverter"_L1;
if (type->isArray()) {
- qDebug() << "Warning: no idea how to handle the Qt5 type " << type->qualifiedCppName();
+ qCWarning(lcShiboken, "Warning: no idea how to handle the Qt type \"%s\"",
+ qPrintable(type->qualifiedCppName()));
return {};
}
/* the typedef'd primitive types case */
auto pte = std::dynamic_pointer_cast<const PrimitiveTypeEntry>(type);
if (!pte) {
- qDebug() << "Warning: the Qt5 primitive type is unknown" << type->qualifiedCppName();
+ qCWarning(lcShiboken, "Warning: the Qt primitive type \"%s\" is unknown",
+ qPrintable(type->qualifiedCppName()));
return {};
}
pte = basicReferencedTypeEntry(pte);
|| value == u"NULLPTR" || value == u"{}";
}
-QString ShibokenGenerator::cpythonCheckFunction(AbstractMetaType metaType)
+QString ShibokenGenerator::cpythonCheckFunction(const AbstractMetaType &metaType)
{
const auto typeEntry = metaType.typeEntry();
if (typeEntry->isCustom()) {
static QLatin1String funcCall("%CPPSELF.%FUNCTION_NAME");
auto pos = str.indexOf(funcCall);
if (pos == -1)
- return QString();
+ return {};
pos = pos + funcCall.size();
while (str.at(pos) == u' ' || str.at(pos) == u'\t')
++pos;
if (str.at(pos) == u'(')
++pos;
- int begin = pos;
+ qsizetype begin = pos;
int counter = 1;
while (counter != 0) {
if (str.at(pos) == u'(')
// Python argument on the binding virtual method.
static const QRegularExpression pyArgsAttributionRegex("%PYARG_(\\d+)\\s*=[^=]\\s*([^;]+)"_L1);
Q_ASSERT(pyArgsAttributionRegex.isValid());
- code.replace(pyArgsAttributionRegex, u"PyTuple_SET_ITEM("_s
+ code.replace(pyArgsAttributionRegex, u"PyTuple_SetItem("_s
+ PYTHON_ARGS + u".object(), \\1-1, \\2)"_s);
- code.replace(pyArgsRegex, u"PyTuple_GET_ITEM("_s
+ code.replace(pyArgsRegex, u"PyTuple_GetItem("_s
+ PYTHON_ARGS + u".object(), \\1-1)"_s);
}
typeSystemConvName().value(converterVariable),
message));
}
- const auto conversionType = conversionTypeO.value();
+ const auto &conversionType = conversionTypeO.value();
QString conversion;
switch (converterVariable) {
case TypeSystemToCppFunction: {
StringStream c(TextStream::Language::Cpp);
- int end = match.capturedStart();
- int start = end;
+ const auto end = match.capturedStart();
+ auto start = end;
while (start > 0 && code.at(start) != u'\n')
--start;
while (code.at(start).isSpace())
bool ShibokenGenerator::useOverrideCaching(const AbstractMetaClassCPtr &metaClass)
{
- return metaClass->isPolymorphic();
+ return checkAttroFunctionNeeds(metaClass).testFlag(AttroCheckFlag::SetattroMethodOverride);
}
-ShibokenGenerator::AttroCheck ShibokenGenerator::checkAttroFunctionNeeds(
- const AbstractMetaClassCPtr &metaClass)
+bool ShibokenGenerator::isVirtualOverride(const AbstractMetaFunctionCPtr &f)
+{
+ return f->isVirtual() && !f->isDestructor() &&
+ ShibokenGenerator::functionGeneration(f).testFlag(FunctionGenerationFlag::VirtualMethod);
+}
+
+ShibokenGenerator::AttroCheck
+ ShibokenGenerator::checkAttroFunctionNeedsImpl(const AbstractMetaClassCPtr &metaClass,
+ const FunctionGroups &functionGroups)
{
- AttroCheck result;
if (metaClass->typeEntry()->isSmartPointer()) {
- result |= AttroCheckFlag::GetattroSmartPointer | AttroCheckFlag::SetattroSmartPointer;
- } else {
- if (getGeneratorClassInfo(metaClass).needsGetattroFunction)
- result |= AttroCheckFlag::GetattroOverloads;
- if (metaClass->queryFirstFunction(metaClass->functions(),
- FunctionQueryOption::GetAttroFunction)) {
- result |= AttroCheckFlag::GetattroUser;
- }
- if (usePySideExtensions() && metaClass->qualifiedCppName() == qObjectT)
- result |= AttroCheckFlag::SetattroQObject;
- if (useOverrideCaching(metaClass))
+ return AttroCheck(AttroCheckFlag::GetattroSmartPointer
+ | AttroCheckFlag::SetattroSmartPointer);
+ }
+
+ AttroCheck result;
+ if (classNeedsGetattroOverloadFunctionImpl(functionGroups))
+ result |= AttroCheckFlag::GetattroOverloads;
+ if (metaClass->queryFirstFunction(metaClass->functions(),
+ FunctionQueryOption::GetAttroFunction)) {
+ result |= AttroCheckFlag::GetattroUser;
+ }
+ if (usePySideExtensions() && metaClass->qualifiedCppName() == qObjectT)
+ result |= AttroCheckFlag::SetattroQObject;
+ if (metaClass->isPolymorphic()) {
+ const auto &funcs = metaClass->functions();
+ if (std::any_of(funcs.cbegin(), funcs.cend(), isVirtualOverride))
result |= AttroCheckFlag::SetattroMethodOverride;
- if (metaClass->queryFirstFunction(metaClass->functions(),
- FunctionQueryOption::SetAttroFunction)) {
- result |= AttroCheckFlag::SetattroUser;
- }
- // PYSIDE-1255: If setattro is generated for a class inheriting
- // QObject, the property code needs to be generated, too.
- if ((result & AttroCheckFlag::SetattroMask) != 0
- && !result.testFlag(AttroCheckFlag::SetattroQObject)
- && isQObject(metaClass)) {
- result |= AttroCheckFlag::SetattroQObject;
- }
+ }
+ if (metaClass->queryFirstFunction(metaClass->functions(),
+ FunctionQueryOption::SetAttroFunction)) {
+ result |= AttroCheckFlag::SetattroUser;
+ }
+ // PYSIDE-1255: If setattro is generated for a class inheriting
+ // QObject, the property code needs to be generated, too.
+ if ((result & AttroCheckFlag::SetattroMask) != 0
+ && !result.testFlag(AttroCheckFlag::SetattroQObject)
+ && isQObject(metaClass)) {
+ result |= AttroCheckFlag::SetattroQObject;
}
return result;
}
-bool ShibokenGenerator::classNeedsGetattroFunctionImpl(const AbstractMetaClassCPtr &metaClass)
+// Returns whether a mixture of static and instance functions exists,
+// requiring a getattro function.
+bool ShibokenGenerator::classNeedsGetattroOverloadFunctionImpl(const FunctionGroups &functionGroup)
{
- if (!metaClass)
- return false;
- if (metaClass->typeEntry()->isSmartPointer())
- return true;
- const auto &functionGroup = getFunctionGroups(metaClass);
for (auto it = functionGroup.cbegin(), end = functionGroup.cend(); it != end; ++it) {
AbstractMetaFunctionCList overloads;
for (const auto &func : std::as_const(it.value())) {
const GeneratorClassInfoCacheEntry &
ShibokenGenerator::getGeneratorClassInfo(const AbstractMetaClassCPtr &scope)
{
- auto cache = generatorClassInfoCache();
+ auto *cache = generatorClassInfoCache();
auto it = cache->find(scope);
if (it == cache->end()) {
it = cache->insert(scope, {});
auto &entry = it.value();
entry.functionGroups = getFunctionGroupsImpl(scope);
- entry.needsGetattroFunction = classNeedsGetattroFunctionImpl(scope);
+ entry.attroCheck = checkAttroFunctionNeedsImpl(scope, entry.functionGroups);
entry.numberProtocolOperators = getNumberProtocolOperators(scope);
entry.boolCastFunctionO = getBoolCast(scope);
}
return getGeneratorClassInfo(scope).boolCastFunctionO;
}
+ShibokenGenerator::AttroCheck
+ ShibokenGenerator::checkAttroFunctionNeeds(const AbstractMetaClassCPtr &scope)
+{
+ Q_ASSERT(scope);
+ return getGeneratorClassInfo(scope).attroCheck;
+}
+
// Use non-const overloads only, for example, "foo()" and "foo()const"
// the second is removed.
static void removeConstOverloads(AbstractMetaFunctionCList *overloads)
funcs.erase(std::find_if(funcs.begin(), funcs.end(), isDecrementOperator));
}
for (const auto &func : funcs) {
- int args;
- if (func->isComparisonOperator()) {
- args = -1;
- } else {
- args = func->arguments().size();
- }
+ const int args = func->isComparisonOperator()
+ ? -1 : func->arguments().size();
auto op = std::make_pair(func->name(), args);
results[op].append(func);
}
// Disambiguate namespaces per module to allow for extending them.
if (type->isNamespace()) {
QString package = type->targetLangPackage();
- const int dot = package.lastIndexOf(u'.');
+ const auto dot = package.lastIndexOf(u'.');
result += QStringView{package}.right(package.size() - (dot + 1));
}
result += _fixedCppTypeName(type->qualifiedCppName());
return result;
}
-void collectfromTypeEntry(TypeEntryCPtr entry, QStringList &typeNames)
+void collectfromTypeEntry(const TypeEntryCPtr &entry, QStringList &typeNames)
{
if (entry->shouldGenerate()) {
typeNames[entry->sbkIndex()] = entry->qualifiedTargetLangName();
int smartPointerCountIndex = getMaxTypeIndex();
for (const auto &smp : api().instantiatedSmartPointers()) {
auto entry = smp.type.typeEntry();
- typeNames[smartPointerCountIndex] = entry->qualifiedTargetLangName();
+ typeNames[smartPointerCountIndex] =
+ smp.specialized->typeEntry()->qualifiedTargetLangName();
++smartPointerCountIndex;
}
}
static QString cpythonTypeNameExt(const TypeEntryCPtr &type);
static QString cpythonTypeNameExt(const AbstractMetaType &type);
static QString cpythonCheckFunction(TypeEntryCPtr type);
- static QString cpythonCheckFunction(AbstractMetaType metaType);
+ static QString cpythonCheckFunction(const AbstractMetaType &metaType);
static QString cpythonIsConvertibleFunction(const TypeEntryCPtr &type);
static QString cpythonIsConvertibleFunction(const AbstractMetaType &metaType);
static QString cpythonIsConvertibleFunction(const AbstractMetaArgument &metaArg);
static QString cpythonToPythonConversionFunction(const AbstractMetaClassCPtr &metaClass);
static QString cpythonToPythonConversionFunction(const TypeEntryCPtr &type);
+ static QString cpythonConstructorName(const AbstractMetaClassCPtr &metaClass);
static QString cpythonFunctionName(const AbstractMetaFunctionCPtr &func) ;
static QString cpythonMethodDefinitionName(const AbstractMetaFunctionCPtr &func);
static QString cpythonGettersSettersDefinitionName(const AbstractMetaClassCPtr &metaClass);
static QList<AbstractMetaFunctionCList>
getNumberProtocolOperators(const AbstractMetaClassCPtr &metaClass);
static BoolCastFunctionOptional getBoolCast(const AbstractMetaClassCPtr &metaClass);
- static bool classNeedsGetattroFunctionImpl(const AbstractMetaClassCPtr &metaClass);
+ static bool classNeedsGetattroOverloadFunctionImpl(const FunctionGroups &functionGroups);
+ static AttroCheck checkAttroFunctionNeedsImpl(const AbstractMetaClassCPtr &metaClass,
+ const FunctionGroups &functionGroups);
+ static bool isVirtualOverride(const AbstractMetaFunctionCPtr &f);
QString translateTypeForWrapperMethod(const AbstractMetaType &cType,
const AbstractMetaClassCPtr &context,
shiboken_compute_python_libraries()
if(PYTHON_LIMITED_API)
- target_compile_definitions(libshiboken PUBLIC "-DPy_LIMITED_API=0x03060000")
+ target_compile_definitions(libshiboken PUBLIC "-DPy_LIMITED_API=0x03080000")
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
#include "helper.h"
#include "pep384ext.h"
#include "sbkconverter.h"
-#include "sbkenum.h"
#include "sbkerrors.h"
#include "sbkfeature_base.h"
-#include "sbkmodule.h"
#include "sbkstring.h"
#include "sbkstaticstrings.h"
#include "sbkstaticstrings_p.h"
+#include "sbktypefactory.h"
#include "autodecref.h"
#include "gilstate.h"
#include <string>
void _destroyParentInfo(SbkObject *obj, bool keepReference);
}
-static void callDestructor(const Shiboken::DtorAccumulatorVisitor::DestructorEntries &dts)
+namespace Shiboken
+{
+// Walk through the first level of non-user-type Sbk base classes relevant for
+// C++ object allocation. Return true from the predicate to terminate.
+template <class Predicate>
+bool walkThroughBases(PyTypeObject *currentType, Predicate predicate)
+{
+ PyObject *bases = currentType->tp_bases;
+ const Py_ssize_t numBases = PyTuple_Size(bases);
+ bool result = false;
+ for (Py_ssize_t i = 0; !result && i < numBases; ++i) {
+ auto *type = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(bases, i));
+ if (PyType_IsSubtype(type, SbkObject_TypeF()) != 0) {
+ result = PepType_SOTP(type)->is_user_type
+ ? walkThroughBases(type, predicate) : predicate(type);
+ }
+ }
+ return result;
+}
+
+int getTypeIndexOnHierarchy(PyTypeObject *baseType, PyTypeObject *desiredType)
+{
+ int index = -1;
+ walkThroughBases(baseType, [&index, desiredType](PyTypeObject *node) {
+ ++index;
+ return PyType_IsSubtype(node, desiredType) != 0;
+ });
+ return index;
+}
+
+int getNumberOfCppBaseClasses(PyTypeObject *baseType)
+{
+ int count = 0;
+ walkThroughBases(baseType, [&count](PyTypeObject *) {
+ ++count;
+ return false;
+ });
+ return count;
+}
+
+std::vector<PyTypeObject *> getCppBaseClasses(PyTypeObject *baseType)
+{
+ std::vector<PyTypeObject *> cppBaseClasses;
+ walkThroughBases(baseType, [&cppBaseClasses](PyTypeObject *node) {
+ cppBaseClasses.push_back(node);
+ return false;
+ });
+ return cppBaseClasses;
+}
+
+using DestructorEntries = std::vector<DestructorEntry>;
+
+DestructorEntries getDestructorEntries(SbkObject *o)
+{
+ DestructorEntries result;
+ void **cptrs = o->d->cptr;
+ walkThroughBases(Py_TYPE(o), [&result, cptrs](PyTypeObject *node) {
+ auto *sotp = PepType_SOTP(node);
+ auto index = result.size();
+ result.push_back(DestructorEntry{sotp->cpp_dtor,
+ cptrs[index]});
+ return false;
+ });
+ return result;
+}
+
+static void callDestructor(const DestructorEntries &dts)
{
for (const auto &e : dts) {
Shiboken::ThreadStateSaver threadSaver;
}
}
+} // namespace Shiboken
+
extern "C"
{
// PYSIDE-939: A general replacement for object_dealloc.
void Sbk_object_dealloc(PyObject *self)
{
- if (PepRuntime_38_flag) {
- // PYSIDE-939: Handling references correctly.
- // This was not needed before Python 3.8 (Python issue 35810)
- Py_DECREF(Py_TYPE(self));
- }
+ // PYSIDE-939: Handling references correctly.
+ // This was not needed before Python 3.8 (Python issue 35810)
+ Py_DECREF(Py_TYPE(self));
+
PepExt_TypeCallFree(self);
}
static PyObject *SbkObjectGetDict(PyObject *pObj, void *)
{
- auto ret = SbkObject_GetDict_NoRef(pObj);
+ auto *ret = SbkObject_GetDict_NoRef(pObj);
Py_XINCREF(ret);
return ret;
}
auto *sbkSelf = reinterpret_cast<SbkObject *>(self);
//Visit children
- Shiboken::ParentInfo *pInfo = sbkSelf->d->parentInfo;
- if (pInfo) {
+ if (auto *pInfo = sbkSelf->d->parentInfo) {
for (SbkObject *c : pInfo->children)
Py_VISIT(c);
}
//Visit refs
- Shiboken::RefCountMap *rInfo = sbkSelf->d->referredObjects;
- if (rInfo) {
- for (auto it = rInfo->begin(), end = rInfo->end(); it != end; ++it)
- Py_VISIT(it->second);
+ if (auto *rInfo = sbkSelf->d->referredObjects) {
+ for (const auto &p : *rInfo)
+ Py_VISIT(p.second);
}
if (sbkSelf->ob_dict)
// is subclassed, that dealloc func will decref (see subtype_dealloc
// in typeobject.c in the python sources)
auto dealloc = PyType_GetSlot(pyType, Py_tp_dealloc);
- bool needTypeDecref = dealloc == SbkDeallocWrapper
- || dealloc == SbkDeallocWrapperWithPrivateDtor;
- if (PepRuntime_38_flag) {
- // PYSIDE-939: Additional rule: Also when a subtype is heap allocated,
- // then the subtype_dealloc deref will be suppressed, and we need again
- // to supply a decref.
- needTypeDecref |= (pyType->tp_base->tp_flags & Py_TPFLAGS_HEAPTYPE) != 0;
- }
+
+ // PYSIDE-939: Additional rule: Also when a subtype is heap allocated,
+ // then the subtype_dealloc deref will be suppressed, and we need again
+ // to supply a decref.
+ const bool needTypeDecref = dealloc == SbkDeallocWrapper
+ || dealloc == SbkDeallocWrapperWithPrivateDtor
+ || (pyType->tp_base->tp_flags & Py_TPFLAGS_HEAPTYPE) != 0;
#if defined(__APPLE__)
// Just checking once that our assumptions are right.
if (sotp->delete_in_main_thread && Shiboken::currentThreadId() != Shiboken::mainThreadId()) {
auto &bindingManager = Shiboken::BindingManager::instance();
if (sotp->is_multicpp) {
- Shiboken::DtorAccumulatorVisitor visitor(sbkObj);
- Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor);
- for (const auto &e : visitor.entries())
+ const auto entries = Shiboken::getDestructorEntries(sbkObj);
+ for (const auto &e : entries)
bindingManager.addToDeletionInMainThread(e);
} else {
Shiboken::DestructorEntry e{sotp->cpp_dtor, sbkObj->d->cptr[0]};
}
}
- PyObject *error_type, *error_value, *error_traceback;
+ PyObject *error_type{};
+ PyObject *error_value{};
+ PyObject *error_traceback{};
/* Save the current exception, if any. */
PyErr_Fetch(&error_type, &error_value, &error_traceback);
if (canDelete) {
if (sotp->is_multicpp) {
- Shiboken::DtorAccumulatorVisitor visitor(sbkObj);
- Shiboken::walkThroughClassHierarchy(Py_TYPE(pyObj), &visitor);
+ const auto entries = Shiboken::getDestructorEntries(sbkObj);
Shiboken::Object::deallocData(sbkObj, true);
- callDestructor(visitor.entries());
+ callDestructor(entries);
} else {
void *cptr = sbkObj->d->cptr[0];
Shiboken::Object::deallocData(sbkObj, true);
if (needTypeDecref)
Py_DECREF(pyType);
- if (PepRuntime_38_flag) {
- // PYSIDE-939: Handling references correctly.
- // This was not needed before Python 3.8 (Python issue 35810)
- Py_DECREF(pyType);
- }
+ // PYSIDE-939: Handling references correctly.
+ // This was not needed before Python 3.8 (Python issue 35810)
+ Py_DECREF(pyType);
}
static inline PyObject *_Sbk_NewVarObject(PyTypeObject *type)
Py_TRASHCAN_SAFE_END(pyObj);
# endif
#endif
- if (PepRuntime_38_flag) {
- // PYSIDE-939: Handling references correctly.
- // This was not needed before Python 3.8 (Python issue 35810)
- Py_DECREF(Py_TYPE(pyObj));
- }
+ // PYSIDE-939: Handling references correctly.
+ // This was not needed before Python 3.8 (Python issue 35810)
+ Py_DECREF(Py_TYPE(pyObj));
}
////////////////////////////////////////////////////////////////////////////
static PyObject *qApp_last = nullptr;
// protecting from multiple application instances
- if (!(type == nullptr || qApp_last == Py_None)) {
+ if (type != nullptr && qApp_last != Py_None) {
const char *res_name = qApp_last != nullptr
? PepType_GetNameStr(Py_TYPE(qApp_last)) : "<Unknown>";
const char *type_name = PepType_GetNameStr(type);
// Before we changed to heap types, it was sufficient to remove the
// Py_TPFLAGS_BASETYPE flag. That does not work, because PySide does
// not respect this flag itself!
- PyObject *name;
- PyObject *pyBases;
- PyObject *dict;
+ PyObject *name{};
+ PyObject *pyBases{};
+ PyObject *dict{};
static const char *kwlist[] = { "name", "bases", "dict", nullptr};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO!O!:sbktype", const_cast<char **>(kwlist),
&PyDict_Type, &dict))
return nullptr;
- for (int i=0, i_max=PyTuple_GET_SIZE(pyBases); i < i_max; i++) {
- PyObject *baseType = PyTuple_GET_ITEM(pyBases, i);
+ for (Py_ssize_t i=0, i_max=PyTuple_Size(pyBases); i < i_max; i++) {
+ PyObject *baseType = PyTuple_GetItem(pyBases, i);
if (PepExt_Type_GetNewSlot(reinterpret_cast<PyTypeObject *>(baseType)) == SbkDummyNew) {
// PYSIDE-595: A base class does not allow inheritance.
return reinterpret_cast<PyTypeObject *>(SbkDummyNew(metatype, args, kwds));
auto *self = reinterpret_cast<SbkObject *>(obSelf);
if (self == nullptr)
return nullptr;
- auto ret = _setupNew(obSelf, subtype);
- auto priv = self->d;
- priv->isQAppSingleton = 1;
+ auto *ret = _setupNew(obSelf, subtype);
+ self->d->isQAppSingleton = 1;
return ret;
}
{
// This is a very simple implementation that supplies a simple identity.
static const char * const opstrings[] = {"<", "<=", "==", "!=", ">", ">="};
- PyObject *res;
+ PyObject *res{};
switch (op) {
namespace Shiboken
{
-bool walkThroughClassHierarchy(PyTypeObject *currentType, HierarchyVisitor *visitor)
-{
- PyObject *bases = currentType->tp_bases;
- Py_ssize_t numBases = PyTuple_GET_SIZE(bases);
- bool result = false;
- for (int i = 0; !result && i < numBases; ++i) {
- auto type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(bases, i));
- if (PyType_IsSubtype(type, reinterpret_cast<PyTypeObject *>(SbkObject_TypeF()))) {
- result = PepType_SOTP(type)->is_user_type
- ? walkThroughClassHierarchy(type, visitor) : visitor->visit(type);
- }
- }
- return result;
-}
// Wrapper metatype and base type ----------------------------------------------------------
-HierarchyVisitor::HierarchyVisitor() = default;
-HierarchyVisitor::~HierarchyVisitor() = default;
-
-bool BaseCountVisitor::visit(PyTypeObject *)
-{
- m_count++;
- return false;
-}
-
-bool BaseAccumulatorVisitor::visit(PyTypeObject *node)
-{
- m_bases.push_back(node);
- return false;
-}
-
-bool GetIndexVisitor::visit(PyTypeObject *node)
-{
- m_index++;
- return PyType_IsSubtype(node, m_desiredType);
-}
-
-bool DtorAccumulatorVisitor::visit(PyTypeObject *node)
-{
- auto *sotp = PepType_SOTP(node);
- m_entries.push_back(DestructorEntry{sotp->cpp_dtor,
- m_pyObject->d->cptr[m_entries.size()]});
- return false;
-}
-
void _initMainThreadId(); // helper.cpp
static std::string msgFailedToInitializeType(const char *description)
// setErrorAboutWrongArguments now gets overload info from the signature module.
// Info can be nullptr and contains extra info.
-void setErrorAboutWrongArguments(PyObject *args, const char *funcName, PyObject *info)
-{
+void setErrorAboutWrongArguments(PyObject *args, const char *funcName, PyObject *info,
+ const char *className)
+{
+ if (className != nullptr) {
+ std::string text = std::string(className);
+ text += '.';
+ text += funcName;
+ SetError_Argument(args, text.c_str(), info);
+ return;
+ }
SetError_Argument(args, funcName, info);
}
-PyObject *returnWrongArguments(PyObject *args, const char *funcName, PyObject *info)
+PyObject *returnWrongArguments(PyObject *args, const char *funcName, PyObject *info,
+ Module::TypeInitStruct initStruct)
{
- setErrorAboutWrongArguments(args, funcName, info);
+ const auto *className = initStruct.fullName ? initStruct.fullName : nullptr;
+ setErrorAboutWrongArguments(args, funcName, info, className);
return {};
}
-int returnWrongArguments_Zero(PyObject *args, const char *funcName, PyObject *info)
+int returnWrongArguments_Zero(PyObject *args, const char *funcName, PyObject *info,
+ Module::TypeInitStruct initStruct)
{
- setErrorAboutWrongArguments(args, funcName, info);
+ const auto *className = initStruct.fullName ? initStruct.fullName : nullptr;
+ setErrorAboutWrongArguments(args, funcName, info, className);
return 0;
}
-int returnWrongArguments_MinusOne(PyObject *args, const char *funcName, PyObject *info)
+int returnWrongArguments_MinusOne(PyObject *args, const char *funcName, PyObject *info,
+ Module::TypeInitStruct initStruct)
{
- setErrorAboutWrongArguments(args, funcName, info);
+ const auto *className = initStruct.fullName ? initStruct.fullName : nullptr;
+ setErrorAboutWrongArguments(args, funcName, info, className);
return -1;
}
return result;
}
-class FindBaseTypeVisitor : public HierarchyVisitor
-{
-public:
- explicit FindBaseTypeVisitor(PyTypeObject *typeToFind) : m_typeToFind(typeToFind) {}
-
- bool visit(PyTypeObject *node) override
- {
- return node == m_typeToFind;
- }
-
-private:
- PyTypeObject *m_typeToFind;
-};
-
std::vector<SbkObject *> splitPyObject(PyObject *pyObj)
{
std::vector<SbkObject *> result;
if (PySequence_Check(pyObj)) {
AutoDecRef lst(PySequence_Fast(pyObj, "Invalid keep reference object."));
if (!lst.isNull()) {
- for (Py_ssize_t i = 0, i_max = PySequence_Fast_GET_SIZE(lst.object()); i < i_max; ++i) {
- PyObject *item = PySequence_Fast_GET_ITEM(lst.object(), i);
+ for (Py_ssize_t i = 0, i_max = PySequence_Size(lst.object()); i < i_max; ++i) {
+ Shiboken::AutoDecRef item(PySequence_GetItem(lst.object(), i));
if (Object::checkType(item))
- result.push_back(reinterpret_cast<SbkObject *>(item));
+ result.push_back(reinterpret_cast<SbkObject *>(item.object()));
}
}
} else {
bool canCallConstructor(PyTypeObject *myType, PyTypeObject *ctorType)
{
- FindBaseTypeVisitor visitor(ctorType);
- if (!walkThroughClassHierarchy(myType, &visitor)) {
+ auto findBasePred = [ctorType](PyTypeObject *type) { return type == ctorType; };
+ if (!walkThroughBases(myType, findBasePred)) {
PyErr_Format(PyExc_TypeError, "%s isn't a direct base class of %s", ctorType->tp_name, myType->tp_name);
return false;
}
PyObject *bases,
unsigned wrapperFlags)
{
- assert(PySequence_Fast_GET_SIZE(bases) > 0);
- typeSpec->slots[0].pfunc = PySequence_Fast_GET_ITEM(bases, 0);
+ assert(PySequence_Size(bases) > 0);
+ typeSpec->slots[0].pfunc = PySequence_GetItem(bases, 0);
auto *type = SbkType_FromSpecBasesMeta(typeSpec, bases, SbkObjectType_TypeF());
- auto sotp = PepType_SOTP(type);
+ auto *sotp = PepType_SOTP(type);
if (wrapperFlags & DeleteInMainThread)
sotp->delete_in_main_thread = 1;
sotp->type_behaviour = (wrapperFlags & Value) != 0
AutoDecRef tpDict(PepType_GetDict(reinterpret_cast<PyTypeObject *>(enclosingObject)));
return PyDict_SetItemString(tpDict, typeName, ob_type) == 0 ? type : nullptr;
}
- assert(PyDict_Check(enclosingObject));
- return PyDict_SetItemString(enclosingObject, typeName, ob_type) == 0 ? type : nullptr;
+ if (PyDict_Check(enclosingObject))
+ return PyDict_SetItemString(enclosingObject, typeName, ob_type) == 0 ? type : nullptr;
}
// PyModule_AddObject steals type's reference.
return d != nullptr && d->mi_specialcast != nullptr;
}
+// Find whether base is a direct single line base class of type
+// (no multiple inheritance), that is, a C++ pointer cast can safely be done.
+static bool isDirectAncestor(PyTypeObject *type, PyTypeObject *base)
+{
+ if (type == base)
+ return true;
+ if (PyTuple_Size(type->tp_bases) == 0)
+ return false;
+ auto *sbkObjectType = SbkObject_TypeF();
+ auto *firstBase = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(type->tp_bases, 0));
+ return firstBase != sbkObjectType
+ && PyType_IsSubtype(type, sbkObjectType) != 0
+ && isDirectAncestor(firstBase, base);
+}
+
+bool canDowncastTo(PyTypeObject *baseType, PyTypeObject *targetType)
+{
+ return isDirectAncestor(targetType, baseType);
+}
+
} // namespace ObjectType
void callCppDestructors(SbkObject *pyObj)
{
- auto priv = pyObj->d;
+ auto *priv = pyObj->d;
if (priv->isQAppSingleton && DestroyQApplication) {
// PYSIDE-1470: Allow to destroy the application from Shiboken.
DestroyQApplication();
PyTypeObject *type = Py_TYPE(pyObj);
auto *sotp = PepType_SOTP(type);
if (sotp->is_multicpp) {
- Shiboken::DtorAccumulatorVisitor visitor(pyObj);
- Shiboken::walkThroughClassHierarchy(type, &visitor);
- callDestructor(visitor.entries());
+ callDestructor(getDestructorEntries(pyObj));
} else {
Shiboken::ThreadStateSaver threadSaver;
threadSaver.save();
}
// If has ref to other objects invalidate all
- if (self->d->referredObjects) {
- RefCountMap &refCountMap = *(self->d->referredObjects);
- for (auto it = refCountMap.begin(), end = refCountMap.end(); it != end; ++it)
- recursive_invalidate(it->second, seen);
+ if (auto *rInfo = self->d->referredObjects) {
+ for (const auto &p : *rInfo)
+ recursive_invalidate(p.second, seen);
}
}
return nullptr;
}
+// Legacy, for compatibility only.
PyObject *newObject(PyTypeObject *instanceType,
void *cptr,
bool hasOwnership,
bool isExactType,
const char *typeName)
{
- // Try to find the exact type of cptr.
- if (!isExactType) {
- if (PyTypeObject *exactType = ObjectType::typeForTypeName(typeName)) {
- instanceType = exactType;
- } else {
- auto resolved = BindingManager::instance().findDerivedType(cptr, instanceType);
- if (resolved.first != nullptr) {
- instanceType = resolved.first;
- cptr = resolved.second;
- }
+ return isExactType
+ ? newObjectForType(instanceType, cptr, hasOwnership)
+ : newObjectWithHeuristics(instanceType, cptr, hasOwnership, typeName);
+}
+
+static PyObject *newObjectWithHeuristicsHelper(PyTypeObject *instanceType,
+ PyTypeObject *exactType,
+ void *cptr,
+ bool hasOwnership)
+{
+ // Try to find the exact type of cptr. For hierarchies with
+ // non-virtual destructors, typeid() will return the base name.
+ // Try type discovery in these cases.
+ if (exactType == nullptr || exactType == instanceType) {
+ auto resolved = BindingManager::instance().findDerivedType(cptr, instanceType);
+ if (resolved.first != nullptr
+ && Shiboken::ObjectType::canDowncastTo(instanceType, resolved.first)) {
+ exactType = resolved.first;
+ cptr = resolved.second;
}
}
+ return newObjectForType(exactType != nullptr ? exactType : instanceType,
+ cptr, hasOwnership);
+}
+
+PyObject *newObjectForPointer(PyTypeObject *instanceType,
+ void *cptr,
+ bool hasOwnership,
+ const char *typeName)
+{
+ // Try to find the exact type of cptr.
+ PyTypeObject *exactType = ObjectType::typeForTypeName(typeName);
+ // PYSIDE-868: In case of multiple inheritance, (for example,
+ // a function returning a QPaintDevice * from a QWidget *),
+ // use instance type to avoid pointer offset errors.
+ return exactType != nullptr && !Shiboken::ObjectType::canDowncastTo(instanceType, exactType)
+ ? newObjectForType(instanceType, cptr, hasOwnership)
+ : newObjectWithHeuristicsHelper(instanceType, exactType, cptr, hasOwnership);
+}
+
+
+PyObject *newObjectWithHeuristics(PyTypeObject *instanceType,
+ void *cptr,
+ bool hasOwnership,
+ const char *typeName)
+{
+ return newObjectWithHeuristicsHelper(instanceType,
+ ObjectType::typeForTypeName(typeName),
+ cptr, hasOwnership);
+}
+
+PyObject *newObjectForType(PyTypeObject *instanceType, void *cptr, bool hasOwnership)
+{
bool shouldCreate = true;
bool shouldRegister = true;
SbkObject *self = nullptr;
+ auto &bindingManager = BindingManager::instance();
// Some logic to ensure that colocated child field does not overwrite the parent
- if (BindingManager::instance().hasWrapper(cptr)) {
- SbkObject *existingWrapper = BindingManager::instance().retrieveWrapper(cptr);
-
+ if (SbkObject *existingWrapper = bindingManager.retrieveWrapper(cptr)) {
self = findColocatedChild(existingWrapper, instanceType);
if (self) {
// Wrapper already registered for cptr.
(!(Shiboken::Object::hasCppWrapper(existingWrapper) ||
Shiboken::Object::hasOwnership(existingWrapper)))) {
// Old wrapper is likely junk, since we have ownership and it doesn't.
- BindingManager::instance().releaseWrapper(existingWrapper);
+ bindingManager.releaseWrapper(existingWrapper);
} else {
// Old wrapper may be junk caused by some bug in identifying object deletion
// but it may not be junk when a colocated field is accessed for an
self->d->cptr[0] = cptr;
self->d->hasOwnership = hasOwnership;
self->d->validCppObject = 1;
- if (shouldRegister) {
- BindingManager::instance().registerWrapper(self, cptr);
- }
+ if (shouldRegister)
+ bindingManager.registerWrapper(self, cptr);
} else {
Py_IncRef(reinterpret_cast<PyObject *>(self));
}
*/
if (PySequence_Check(child) && !Object::checkType(child)) {
Shiboken::AutoDecRef seq(PySequence_Fast(child, nullptr));
- for (Py_ssize_t i = 0, max = PySequence_Size(seq); i < max; ++i)
- setParent(parent, PySequence_Fast_GET_ITEM(seq.object(), i));
+ for (Py_ssize_t i = 0, max = PySequence_Size(seq); i < max; ++i) {
+ Shiboken::AutoDecRef obj(PySequence_GetItem(seq.object(), i));
+ setParent(parent, obj);
+ }
return;
}
return o == nullptr || o == Py_None;
}
-static void removeRefCountKey(SbkObject *self, const char *key)
+static void removeRefCountKey(SbkObject *self, const std::string &key)
{
if (self->d->referredObjects) {
const auto iterPair = self->d->referredObjects->equal_range(key);
}
}
-void keepReference(SbkObject *self, const char *key, PyObject *referredObject, bool append)
+void keepReference(SbkObject *self, const char *keyC, PyObject *referredObject, bool append)
{
+ std::string key(keyC);
+
if (isNone(referredObject)) {
removeRefCountKey(self, key);
return;
void removeReference(SbkObject *self, const char *key, PyObject *referredObject)
{
if (!isNone(referredObject))
- removeRefCountKey(self, key);
+ removeRefCountKey(self, std::string(key));
}
void clearReferences(SbkObject *self)
#include "sbkpython.h"
#include "shibokenmacros.h"
-#include "sbktypefactory.h"
+#include "sbkmodule.h"
#include <vector>
#include <string>
/// setErrorAboutWrongArguments now gets overload information from the signature module.
/// The extra info argument can contain additional data about the error.
LIBSHIBOKEN_API void setErrorAboutWrongArguments(PyObject *args, const char *funcName,
- PyObject *info);
+ PyObject *info, const char *className = nullptr);
-/// Return values for the different retun variants.
+/// Return values for the different return variants.
/// This is used instead of goto.
-LIBSHIBOKEN_API PyObject *returnWrongArguments(PyObject *args, const char *funcName,
- PyObject *info);
+/// Either funcname should contain the full function name, or the module and class
+/// are taken from the TypeInitStruct.
+LIBSHIBOKEN_API PyObject *returnWrongArguments(PyObject *args, const char *funcName, PyObject *info,
+ Module::TypeInitStruct initStruct = {nullptr, nullptr});
-LIBSHIBOKEN_API int returnWrongArguments_Zero(PyObject *args, const char *funcName,
- PyObject *info);
+LIBSHIBOKEN_API int returnWrongArguments_Zero(PyObject *args, const char *funcName, PyObject *info,
+ Module::TypeInitStruct initStruct = {nullptr, nullptr});
-LIBSHIBOKEN_API int returnWrongArguments_MinusOne(PyObject *args, const char *funcName,
- PyObject *info);
+LIBSHIBOKEN_API int returnWrongArguments_MinusOne(PyObject *args, const char *funcName, PyObject *info,
+ Module::TypeInitStruct initStruct = {nullptr, nullptr});
/// A simple special version for the end of rich comparison.
LIBSHIBOKEN_API PyObject *returnFromRichCompare(PyObject *result);
* \since 5.12
*/
LIBSHIBOKEN_API bool hasSpecialCastFunction(PyTypeObject *sbkType);
+
+/// Returns whether a C++ pointer of \p baseType can be safely downcast
+/// to \p targetType (base is a direct, single line base class of targetType).
+/// (is a direct, single-line inheritance)
+/// \param baseType Python type of base class
+/// \param targetType Python type of derived class
+/// \since 6.8
+LIBSHIBOKEN_API bool canDowncastTo(PyTypeObject *baseType, PyTypeObject *targetType);
}
namespace Object {
const PyTypeObject *instanceType);
/**
- * Bind a C++ object to Python.
+ * Bind a C++ object to Python. Forwards to
+ * newObjectWithHeuristics(), newObjectForType() depending on \p isExactType.
* \param instanceType equivalent Python type for the C++ object.
* \param hasOwnership if true, Python will try to delete the underlying C++ object when there's no more refs.
* \param isExactType if false, Shiboken will use some heuristics to detect the correct Python type of this C++
bool isExactType = false,
const char *typeName = nullptr);
+/// Bind a C++ object to Python for polymorphic pointers. Calls
+/// newObjectWithHeuristics() with an additional check for multiple
+/// inheritance, in which case it will fall back to instanceType.
+/// \param instanceType Equivalent Python type for the C++ object.
+/// \param hasOwnership if true, Python will try to delete the underlying C++ object
+/// when there's no more refs.
+/// \param typeName If non-null, this will be used as helper to find the correct
+/// Python type for this object (obtained by typeid().name().
+LIBSHIBOKEN_API PyObject *newObjectForPointer(PyTypeObject *instanceType,
+ void *cptr,
+ bool hasOwnership = true,
+ const char *typeName = nullptr);
+
+/// Bind a C++ object to Python using some heuristics to detect the correct
+/// Python type of this C++ object. In any case \p instanceType must be provided;
+/// it'll be used as search starting point and as fallback.
+/// \param instanceType Equivalent Python type for the C++ object.
+/// \param hasOwnership if true, Python will try to delete the underlying C++ object
+/// C++ object when there are no more references.
+/// when there's no more refs.
+/// \param typeName If non-null, this will be used as helper to find the correct
+/// Python type for this object (obtained by typeid().name().
+LIBSHIBOKEN_API PyObject *newObjectWithHeuristics(PyTypeObject *instanceType,
+ void *cptr,
+ bool hasOwnership = true,
+ const char *typeName = nullptr);
+
+/// Bind a C++ object to Python using the given type.
+/// \param instanceType Equivalent Python type for the C++ object.
+/// \param hasOwnership if true, Python will try to delete the underlying
+/// C++ object when there are no more references.
+LIBSHIBOKEN_API PyObject *newObjectForType(PyTypeObject *instanceType,
+ void *cptr, bool hasOwnership = true);
+
/**
* Changes the valid flag of a PyObject, invalid objects will raise an exception when someone tries to access it.
*/
/// True if this type holds two or more C++ instances, e.g.: a Python class which inherits from two C++ classes.
unsigned int is_multicpp : 1;
- /// True if this type was defined by the user.
+ /// True if this type was defined by the user (a class written in Python inheriting
+ /// a class provided by a Shiboken binding).
unsigned int is_user_type : 1;
/// Tells is the type is a value type or an object-type, see BEHAVIOUR_ *constants.
unsigned int type_behaviour : 2;
**/
std::vector<SbkObject *> splitPyObject(PyObject *pyObj);
-/**
-* Visitor class used by walkOnClassHierarchy function.
-*/
-class HierarchyVisitor
-{
-public:
- HierarchyVisitor(const HierarchyVisitor &) = delete;
- HierarchyVisitor(HierarchyVisitor &&) = delete;
- HierarchyVisitor &operator=(const HierarchyVisitor &) = delete;
- HierarchyVisitor &operator=(HierarchyVisitor &&) = delete;
-
- HierarchyVisitor();
- virtual ~HierarchyVisitor();
-
- virtual bool visit(PyTypeObject *node) = 0; // return true to terminate
-};
-
-class BaseCountVisitor : public HierarchyVisitor
-{
-public:
- bool visit(PyTypeObject *) override;
-
- int count() const { return m_count; }
-
-private:
- int m_count = 0;
-};
-
-class BaseAccumulatorVisitor : public HierarchyVisitor
-{
-public:
- using Result = std::vector<PyTypeObject *>;
-
- bool visit(PyTypeObject *node) override;
-
- Result bases() const { return m_bases; }
-
-private:
- Result m_bases;
-};
-
-class GetIndexVisitor : public HierarchyVisitor
-{
-public:
- explicit GetIndexVisitor(PyTypeObject *desiredType) : m_desiredType(desiredType) {}
-
- bool visit(PyTypeObject *node) override;
-
- int index() const { return m_index; }
-
-private:
- int m_index = -1;
- PyTypeObject *m_desiredType;
-};
-
-/// Collect destructors and C++ instances of each C++ object held by a Python
-/// object
-class DtorAccumulatorVisitor : public HierarchyVisitor
-{
-public:
- explicit DtorAccumulatorVisitor(SbkObject *pyObj) : m_pyObject(pyObj) {}
-
- bool visit(PyTypeObject *node) override;
-
- using DestructorEntries = std::vector<DestructorEntry>;
-
- const DestructorEntries &entries() const { return m_entries; }
-
-private:
- DestructorEntries m_entries;
- SbkObject *m_pyObject;
-};
-
-/// \internal Internal function used to walk on classes inheritance trees.
-/**
-* Walk on class hierarchy using a DFS algorithm.
-* For each pure Shiboken type found, HierarchyVisitor::visit is called and the algorithm
-* considers all children of this type as visited.
-*/
-bool walkThroughClassHierarchy(PyTypeObject *currentType, HierarchyVisitor *visitor);
-
-inline int getTypeIndexOnHierarchy(PyTypeObject *baseType, PyTypeObject *desiredType)
-{
- GetIndexVisitor visitor(desiredType);
- walkThroughClassHierarchy(baseType, &visitor);
- return visitor.index();
-}
-
-inline int getNumberOfCppBaseClasses(PyTypeObject *baseType)
-{
- BaseCountVisitor visitor;
- walkThroughClassHierarchy(baseType, &visitor);
- return visitor.count();
-}
-
-inline std::vector<PyTypeObject *> getCppBaseClasses(PyTypeObject *baseType)
-{
- BaseAccumulatorVisitor visitor;
- walkThroughClassHierarchy(baseType, &visitor);
- return visitor.bases();
-}
+int getNumberOfCppBaseClasses(PyTypeObject *baseType);
namespace Object
{
}
}
-BindingManager::BindingManager()
+BindingManager::BindingManager() :
+ m_d(new BindingManager::BindingManagerPrivate)
{
- m_d = new BindingManager::BindingManagerPrivate;
-
#ifdef SHIBOKEN_INSTALL_FREE_DEBUG_HOOK
debugInstallFreeHook();
#endif
return method;
}
- PyObject *method = PyObject_GetAttr(reinterpret_cast<PyObject *>(wrapper), pyMethodName);
+ PyObject *method = PyObject_GetAttr(obWrapper, pyMethodName);
PyObject *function = nullptr;
if (method) {
// PYSIDE-535: This macro is redefined in a compatible way in pep384
if (PyMethod_Check(method)) {
- if (PyMethod_GET_SELF(method) == reinterpret_cast<PyObject *>(wrapper)) {
+ if (PyMethod_GET_SELF(method) == obWrapper) {
function = PyMethod_GET_FUNCTION(method);
} else {
Py_DECREF(method);
// Not retaining a reference inline with what PyMethod_GET_SELF does.
Py_DECREF(im_self);
- if (im_self == reinterpret_cast<PyObject *>(wrapper)) {
+ if (im_self == obWrapper) {
function = PyObject_GetAttr(method, PyName::im_func());
// Not retaining a reference inline with what PyMethod_GET_FUNCTION does.
Py_DECREF(function);
}
if (method != nullptr) {
- PyObject *defaultMethod{};
PyObject *mro = Py_TYPE(wrapper)->tp_mro;
- int size = PyTuple_GET_SIZE(mro);
+ int size = PyTuple_Size(mro);
bool defaultFound = false;
// The first class in the mro (index 0) is the class being checked and it should not be tested.
// The last class in the mro (size - 1) is the base Python object class which should not be tested also.
for (int idx = 1; idx < size - 1; ++idx) {
- auto *parent = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
- AutoDecRef tpDict(PepType_GetDict(parent));
- auto *parentDict = tpDict.object();
+ auto *parent = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
+ AutoDecRef parentDict(PepType_GetDict(parent));
if (parentDict) {
- defaultMethod = PyDict_GetItem(parentDict, pyMethodName);
- if (defaultMethod) {
+ if (PyObject *defaultMethod = PyDict_GetItem(parentDict.object(), pyMethodName)) {
defaultFound = true;
if (function != defaultMethod)
return method;
return !ObjectType::checkType(type) || ObjectType::isUserType(type);
}
-bool callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds,
- const char *fullName)
+static bool _callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds,
+ std::string_view className)
{
using Shiboken::AutoDecRef;
auto *startType = Py_TYPE(self);
auto *mro = startType->tp_mro;
- Py_ssize_t idx, n = PyTuple_GET_SIZE(mro);
- auto classNameLen = std::strrchr(fullName, '.') - fullName;
+ Py_ssize_t idx = 0;
+ const Py_ssize_t n = PyTuple_Size(mro);
/* No need to check the last one: it's gonna be skipped anyway. */
- for (idx = 0; idx + 1 < n; ++idx) {
- auto *lookType = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
- const char *lookName = lookType->tp_name;
- auto lookLen = long(std::strlen(lookName));
- if (std::strncmp(lookName, fullName, classNameLen) == 0 && lookLen == classNameLen)
+ for ( ; idx + 1 < n; ++idx) {
+ auto *lookType = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
+ if (className == lookType->tp_name)
break;
}
// We are now at the first non-Python class `QObject`.
// mro: ('C', 'A', 'QObject', 'Object', 'B', 'object')
// We want to catch class `B` and call its `__init__`.
for (idx += 1; idx + 1 < n; ++idx) {
- auto *t = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
+ auto *t = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
if (isPythonType(t))
break;
}
if (idx >= n)
return false;
- auto *obSubType = PyTuple_GET_ITEM(mro, idx);
+ auto *obSubType = PyTuple_GetItem(mro, idx);
auto *subType = reinterpret_cast<PyTypeObject *>(obSubType);
if (subType == &PyBaseObject_Type)
return false;
AutoDecRef newArgs(PyTuple_New(1));
auto *newArgsOb = newArgs.object();
Py_INCREF(self);
- PyTuple_SET_ITEM(newArgsOb, 0, self);
+ PyTuple_SetItem(newArgsOb, 0, self);
// Note: This can fail, so please always check the error status.
AutoDecRef result(PyObject_Call(func, newArgs, kwds));
return true;
}
-} // namespace Shiboken
+bool callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds,
+ const char *fullName)
+{
+ // fullName is the full dotted name of module, class and function.
+ // We need to cut off the rightmost field to get the module.class name.
+ auto className = std::string_view(fullName);
+ auto pos = className.rfind('.');
+ assert(pos != std::string_view::npos);
+ className = className.substr(0, pos);
+ return _callInheritedInit(self, args, kwds, className);
+}
+bool callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds,
+ Module::TypeInitStruct typeStruct)
+{
+ // TypeInitStruct must contain the module.class name.
+ return _callInheritedInit(self, args, kwds, typeStruct.fullName);
+}
+
+} // namespace Shiboken
LIBSHIBOKEN_API bool callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds,
const char *fullName);
+LIBSHIBOKEN_API bool callInheritedInit(PyObject *self, PyObject *args, PyObject *kwds,
+ Module::TypeInitStruct typeStruct);
} // namespace Shiboken
#endif // BINDINGMANAGER_H
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
embedding_generator.py
sys.path.insert(0, os.fspath(build_script_dir))
-from build_scripts import utils
+from build_scripts import utils # noqa: E402
def runpy(cmd, **kw):
* {remark}.
*/
"""), file=fout)
- headsize = ( 0 if is_text else
- 16 if sys.version_info >= (3, 7) else 12 if sys.version_info >= (3, 3) else 8)
+ headsize = 0 if is_text else 16
binstr = fin.read()[headsize:]
if is_text:
try:
print(file=fout)
use_ord = sys.version_info[0] == 2
for i in range(0, len(binstr), 16):
- for c in bytes(binstr[i : i + 16]):
+ for c in bytes(binstr[i: i + 16]):
ord_c = ord(c) if use_ord else c
print(f"{ord_c:#4},", file=fout, end="")
print(file=fout)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
module_collector.py
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
signature_bootstrap.py
return sys.meta_path, EmbeddableZipImporter(vzip)
-class EmbeddableZipImporter(object):
+class EmbeddableZipImporter:
def __init__(self, zip_file):
def p2m(filename):
}
} // namespace Shiboken
-
} // namespace Shiboken
#endif // GILSTATE_H
-
if (i)
str << ", ";
str << '(';
- PyObject *item = PySequence_GetItem(obj, i);
+ Shiboken::AutoDecRef item(PySequence_GetItem(obj, i));
formatPyObject(item, str);
str << ')';
- Py_XDECREF(item);
}
if (printSize < size)
str << ",...";
// Check all items
Shiboken::AutoDecRef args(PySequence_Fast(argList, nullptr));
- int numArgs = int(PySequence_Fast_GET_SIZE(argList));
- for (int i = 0; i < numArgs; ++i) {
+ Py_ssize_t numArgs = PySequence_Size(argList);
+ for (Py_ssize_t i = 0; i < numArgs; ++i) {
PyObject *item = PyList_GET_ITEM(args.object(), i);
if (!PyBytes_Check(item) && !PyUnicode_Check(item))
return false;
if (seq.isNull())
return nullptr;
- Py_ssize_t size = PySequence_Fast_GET_SIZE(seq.object());
+ Py_ssize_t size = PySequence_Size(seq.object());
int *array = new int[size + (zeroTerminated ? 1 : 0)];
for (int i = 0; i < size; i++) {
- PyObject *item = PySequence_Fast_GET_ITEM(seq.object(), i);
+ Shiboken::AutoDecRef item(PySequence_GetItem(seq.object(), i));
if (!PyLong_Check(item)) {
PyErr_SetString(PyExc_TypeError, "Sequence of ints expected");
delete[] array;
PepExt_Type_GetFreeSlot(Py_TYPE(object))(object);
}
+LIBSHIBOKEN_API bool PepExt_Weakref_IsAlive(PyObject *weakRef);
+
#endif // PEP384EXT_H
static PyObject *
find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
{
- Py_ssize_t i, n;
- PyObject *mro, *res, *base;
-
- /* Look in tp_dict of types in MRO */
- mro = type->tp_mro;
-
- res = nullptr;
- /* Keep a strong reference to mro because type->tp_mro can be replaced
- during dict lookup, e.g. when comparing to non-string keys. */
- Py_INCREF(mro);
- assert(PyTuple_Check(mro));
- n = PyTuple_GET_SIZE(mro);
- for (i = 0; i < n; i++) {
- base = PyTuple_GET_ITEM(mro, i);
+ *error = 0;
+ /* Look in tp_dict of types in MRO. Keep a strong reference to mro because
+ * type->tp_mro can be replaced during dict lookup, e.g. when comparing to
+ * non-string keys. */
+ assert(PyTuple_Check(type->tp_mro));
+ Py_INCREF(type->tp_mro);
+ Shiboken::AutoDecRef mro(type->tp_mro);
+ for (Py_ssize_t i = 0, n = PyTuple_Size(mro.object()); i < n; ++i) {
+ auto *base = PyTuple_GetItem(mro.object(), i);
assert(PyType_Check(base));
auto *type = reinterpret_cast<PyTypeObject *>(base);
Shiboken::AutoDecRef dict(PepType_GetDict(type));
assert(!dict.isNull() && PyDict_Check(dict.object()));
- res = PyDict_GetItem(dict.object(), name);
- if (res != nullptr)
- break;
+ if (auto *res = PyDict_GetItem(dict.object(), name))
+ return res;
if (PyErr_Occurred()) {
*error = -1;
- goto done;
+ return nullptr;
}
}
- *error = 0;
-done:
- Py_DECREF(mro);
- return res;
+ return nullptr;
}
/* Internal API to look for a name through the MRO.
PyObject *
_PepType_Lookup(PyTypeObject *type, PyObject *name)
{
- PyObject *res;
- int error;
-
/* We may end up clearing live exceptions below, so make sure it's ours. */
assert(!PyErr_Occurred());
- res = find_name_in_mro(type, name, &error);
- /* Only put NULL results into cache if there was no error. */
+ int error{};
+ auto *res = find_name_in_mro(type, name, &error);
+ /* Only put nullptr results into cache if there was no error. */
if (error) {
- /* It's not ideal to clear the error condition,
- but this function is documented as not setting
- an exception, and I don't want to change that.
- E.g., when PyType_Ready() can't proceed, it won't
- set the "ready" flag, so future attempts to ready
- the same type will call it again -- hopefully
- in a context that propagates the exception out.
- */
- if (error == -1) {
+ /* It's not ideal to clear the error condition, but this function is
+ * documented as not setting an exception, and I don't want to change
+ * that. E.g., when PyType_Ready() can't proceed, it won't set the
+ * "ready" flag, so future attempts to ready the same type will call
+ * it again -- hopefully in a context that propagates the exception out. */
+ if (error == -1)
PyErr_Clear();
- }
return nullptr;
}
return res;
*/
#ifdef Py_LIMITED_API
-static PyObject *sys_flags = nullptr;
-
int
Pep_GetFlag(const char *name)
{
+ static PyObject *sys_flags = nullptr;
static int initialized = 0;
- int ret = -1;
if (!initialized) {
sys_flags = PySys_GetObject("flags");
initialized = 1;
}
if (sys_flags != nullptr) {
- PyObject *ob_ret = PyObject_GetAttrString(sys_flags, name);
- if (ob_ret != nullptr) {
- long long_ret = PyLong_AsLong(ob_ret);
- Py_DECREF(ob_ret);
- ret = (int) long_ret;
- }
+ Shiboken::AutoDecRef ob_ret(PyObject_GetAttrString(sys_flags, name));
+ if (!ob_ret.isNull())
+ return int(PyLong_AsLong(ob_ret.object()));
}
- return ret;
+ return -1;
}
int
PyErr_Restore(type, value, traceback);
return value;
}
+
+struct PepException_HEAD
+{
+ PyObject_HEAD
+ PyObject *x1; // dict
+ PyObject *args;
+};
+
+// PyException_GetArgs/PyException_SetArgs were added to the stable API in 3.12
+PyObject *PepException_GetArgs(PyObject *ex)
+{
+ auto *h = reinterpret_cast<PepException_HEAD *>(ex);
+ Py_XINCREF(h->args);
+ return h->args;
+}
+
+LIBSHIBOKEN_API void PepException_SetArgs(PyObject *ex, PyObject *args)
+{
+ auto *h = reinterpret_cast<PepException_HEAD *>(ex);
+ Py_XINCREF(args);
+ auto *old = h->args; // Py_XSETREF()
+ h->args = args;
+ Py_XDECREF(old);
+
+}
#endif // Limited or < 3.12
/*****************************************************************************
int
PepCode_Get(PepCodeObject *co, const char *name)
{
- PyObject *ob = reinterpret_cast<PyObject *>(co);
- PyObject *ob_ret;
- int ret = -1;
-
- ob_ret = PyObject_GetAttrString(ob, name);
- if (ob_ret != nullptr) {
- long long_ret = PyLong_AsLong(ob_ret);
- Py_DECREF(ob_ret);
- ret = (int) long_ret;
- }
- return ret;
+ auto *ob = reinterpret_cast<PyObject *>(co);
+ Shiboken::AutoDecRef ob_ret(PyObject_GetAttrString(ob, name));
+ return ob_ret.isNull() ? -1 : int(PyLong_AsLong(ob_ret.object()));
}
int PepCode_Check(PyObject *o)
int
PyDateTime_Get(PyObject *ob, const char *name)
{
- PyObject *ob_ret;
- int ret = -1;
-
- ob_ret = PyObject_GetAttrString(ob, name);
- if (ob_ret != nullptr) {
- long long_ret = PyLong_AsLong(ob_ret);
- Py_DECREF(ob_ret);
- ret = (int) long_ret;
- }
- return ret;
+ Shiboken::AutoDecRef ob_ret(PyObject_GetAttrString(ob, name));
+ return ob_ret.isNull() ? -1 : int(PyLong_AsLong(ob_ret.object()));
}
PyObject *
PyDate_FromDate(int year, int month, int day)
{
- return PyObject_CallFunction((PyObject *)PyDateTimeAPI->DateType,
- (char *)"(iii)", year, month, day);
+ auto *ob = reinterpret_cast<PyObject *>(PyDateTimeAPI->DateType);
+ return PyObject_CallFunction(ob, "(iii)", year, month, day);
}
PyObject *
PyDateTime_FromDateAndTime(int year, int month, int day,
int hour, int min, int sec, int usec)
{
- return PyObject_CallFunction((PyObject *)PyDateTimeAPI->DateTimeType,
- (char *)"(iiiiiii)", year, month, day,
- hour, min, sec, usec);
+ auto *ob = reinterpret_cast<PyObject *>(PyDateTimeAPI->DateTimeType);
+ return PyObject_CallFunction(ob, "(iiiiiii)", year, month, day,
+ hour, min, sec, usec);
}
PyObject *
PyObject *
PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals)
{
- PyObject *code = Py_CompileString(str, "pyscript", start);
- PyObject *ret = nullptr;
-
- if (code != nullptr) {
- ret = PyEval_EvalCode(code, globals, locals);
- }
- Py_XDECREF(code);
- return ret;
+ Shiboken::AutoDecRef code(Py_CompileString(str, "pyscript", start));
+ return code.isNull() ? nullptr : PyEval_EvalCode(code.object(), globals, locals);
}
#endif // Py_LIMITED_API
static PyTypeObject *getMethodType(void)
{
- static const char prog[] =
- "class _C:\n"
- " def _m(self): pass\n"
- "result = type(_C()._m)\n";
+ static const char prog[] = R"(class _C:
+ def _m(self): pass
+result = type(_C()._m)
+)";
return reinterpret_cast<PyTypeObject *>(PepRun_GetResult(prog));
}
PyObject *
PyMethod_New(PyObject *func, PyObject *self)
{
- return PyObject_CallFunction((PyObject *)PepMethod_TypePtr,
- (char *)"(OO)", func, self);
+ auto *ob = reinterpret_cast<PyObject *>(PepMethod_TypePtr);
+ return PyObject_CallFunction(ob, "(OO)", func, self);
}
PyObject *
PyMethod_Function(PyObject *im)
{
- PyObject *ret = PyObject_GetAttr(im, Shiboken::PyMagicName::func());
-
// We have to return a borrowed reference.
+ PyObject *ret = PyObject_GetAttr(im, Shiboken::PyMagicName::func());
Py_DECREF(ret);
return ret;
}
PyObject *
PyMethod_Self(PyObject *im)
{
- PyObject *ret = PyObject_GetAttr(im, Shiboken::PyMagicName::self());
-
// We have to return a borrowed reference.
// If we don't obey that here, then we get a test error!
+ PyObject *ret = PyObject_GetAttr(im, Shiboken::PyMagicName::self());
Py_DECREF(ret);
return ret;
}
PyObject *
PepFunction_Get(PyObject *ob, const char *name)
{
- PyObject *ret;
-
// We have to return a borrowed reference.
- ret = PyObject_GetAttrString(ob, name);
+ PyObject *ret = PyObject_GetAttrString(ob, name);
Py_XDECREF(ret);
return ret;
}
"from types import FunctionType as result\n";
return reinterpret_cast<PyTypeObject *>(PepRun_GetResult(prog));
}
-#endif // Py_LIMITED_API || Python 2
+#endif // Py_LIMITED_API
/*****************************************************************************
*
{
const char *ret = type->tp_name;
const char *nodots = std::strrchr(ret, '.');
- if (nodots)
- ret = nodots + 1;
- return ret;
+ return nodots != nullptr ? nodots + 1 : ret;
}
// PYSIDE-2264: Find the _functools or functools module and retrieve the
PyObject *
PyImport_GetModule(PyObject *name)
{
- PyObject *m;
PyObject *modules = PyImport_GetModuleDict();
- if (modules == NULL) {
+ if (modules == nullptr) {
PyErr_SetString(PyExc_RuntimeError, "unable to get sys.modules");
- return NULL;
+ return nullptr;
}
Py_INCREF(modules);
+ PyObject *m{};
if (PyDict_CheckExact(modules)) {
m = PyDict_GetItemWithError(modules, name); /* borrowed */
Py_XINCREF(m);
- }
- else {
+ } else {
m = PyObject_GetItem(modules, name);
- if (m == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) {
+ if (m == nullptr && PyErr_ExceptionMatches(PyExc_KeyError))
PyErr_Clear();
- }
}
Py_DECREF(modules);
return m;
/*
* Evaluate a string and return the variable `result`
*/
- PyObject *d, *v, *res;
-
- d = PyDict_New();
+ PyObject *d = PyDict_New();
if (d == nullptr
|| PyDict_SetItem(d, Shiboken::PyMagicName::builtins(), PyEval_GetBuiltins()) < 0) {
return nullptr;
}
- v = PyRun_String(command, Py_file_input, d, d);
- res = v ? PyDict_GetItem(d, Shiboken::PyName::result()) : nullptr;
+ PyObject *v = PyRun_String(command, Py_file_input, d, d);
+ PyObject *res = v ? PyDict_GetItem(d, Shiboken::PyName::result()) : nullptr;
Py_XDECREF(v);
Py_DECREF(d);
return res;
PyTypeObject *PepType_Type_tp_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
{
- auto ret = PyType_Type.tp_new(metatype, args, kwds);
+ auto *ret = PyType_Type.tp_new(metatype, args, kwds);
return reinterpret_cast<PyTypeObject *>(ret);
}
*
*/
-#ifdef Py_LIMITED_API
-// We keep these definitions local, because they don't work in Python 2.
-# define PyUnicode_GET_LENGTH(op) PyUnicode_GetLength((PyObject *)(op))
-# define PyUnicode_READ_CHAR(u, i) PyUnicode_ReadChar((PyObject *)(u), (i))
-#endif // Py_LIMITED_API
-
PyObject *
_Pep_PrivateMangle(PyObject *self, PyObject *name)
{
* This function is modelled after _Py_Mangle, but is optimized
* a little for our purpose.
*/
- if (PyUnicode_READ_CHAR(name, 0) != '_' ||
- PyUnicode_READ_CHAR(name, 1) != '_') {
+ if (PyUnicode_ReadChar(name, 0) != '_' ||
+ PyUnicode_ReadChar(name, 1) != '_') {
Py_INCREF(name);
return name;
}
- size_t nlen = PyUnicode_GET_LENGTH(name);
+ const Py_ssize_t nlen = PyUnicode_GetLength(name);
/* Don't mangle __id__ or names with dots. */
- if ((PyUnicode_READ_CHAR(name, nlen-1) == '_' &&
- PyUnicode_READ_CHAR(name, nlen-2) == '_') ||
+ if ((PyUnicode_ReadChar(name, nlen-1) == '_' &&
+ PyUnicode_ReadChar(name, nlen-2) == '_') ||
PyUnicode_FindChar(name, '.', 0, nlen, 1) != -1) {
Py_INCREF(name);
return name;
// PYSIDE-1436: _Py_Mangle is no longer exposed; implement it always.
// The rest of this function is our own implementation of _Py_Mangle.
// Please compare the original function in compile.c .
- size_t plen = PyUnicode_GET_LENGTH(privateobj.object());
+ Py_ssize_t plen = PyUnicode_GetLength(privateobj.object());
/* Strip leading underscores from class name */
- size_t ipriv = 0;
- while (PyUnicode_READ_CHAR(privateobj.object(), ipriv) == '_')
+ Py_ssize_t ipriv = 0;
+ while (PyUnicode_ReadChar(privateobj.object(), ipriv) == '_')
ipriv++;
if (ipriv == plen) {
Py_INCREF(name);
"private identifier too large to be mangled");
return nullptr;
}
- size_t const amount = ipriv + 1 + plen + nlen;
- size_t const big_stack = 1000;
+ const Py_ssize_t amount = ipriv + 1 + plen + nlen;
+ const Py_ssize_t big_stack = 1000;
wchar_t bigbuf[big_stack];
- wchar_t *resbuf = amount <= big_stack ? bigbuf : (wchar_t *)malloc(sizeof(wchar_t) * amount);
+ wchar_t *resbuf = amount <= big_stack
+ ? bigbuf : reinterpret_cast<wchar_t *>(malloc(sizeof(wchar_t) * amount));
if (!resbuf)
return nullptr;
/* ident = "_" + priv[ipriv:] + ident # i.e. 1+plen+nlen bytes */
*
*/
-int PepRuntime_38_flag = 0;
-
-static void
-init_PepRuntime()
-{
- // We expect a string of the form "\d\.\d+\."
- const char *version = Py_GetVersion();
- if (version[0] < '3')
- return;
- if (std::atoi(version + 2) >= 8)
- PepRuntime_38_flag = 1;
-}
-
static long _GetPepRuntimeVersion()
{
auto *version = PySys_GetObject("version_info");
if (enumType == SETP_key)
return SETP_value;
auto it = SETP_extender.find(enumType);
- if (it == SETP_extender.end()) {
- it = SETP_extender.insert({enumType, {}}).first;
- memset(&it->second, 0, sizeof(SbkEnumTypePrivate));
- }
+ if (it == SETP_extender.end())
+ it = SETP_extender.insert({enumType, SbkEnumTypePrivate{nullptr, nullptr}}).first;
SETP_key = enumType;
SETP_value = &it->second;
return SETP_value;
void
Pep384_Init()
{
- init_PepRuntime();
#ifdef Py_LIMITED_API
check_PyTypeObject_valid();
Pep_GetVerboseFlag();
}
} // extern "C"
+
+LIBSHIBOKEN_API bool PepExt_Weakref_IsAlive(PyObject *weakRef)
+{
+#if defined(Py_LIMITED_API) || PY_VERSION_HEX < 0x030D0000
+ return PyWeakref_GetObject(weakRef) != Py_None;
+#else
+ // FIXME: Make this the default code path once Limited API has been raised to 3.13
+ // Note: PyWeakref_GetObject() will be removed in 3.15.
+ PyObject *pobj = nullptr;
+ const bool result = PyWeakref_GetRef(weakRef, &pobj) == 1;
+ Py_XDECREF(pobj);
+ return result;
+#endif
+}
#ifndef PEP384IMPL_H
#define PEP384IMPL_H
+#include "shibokenmacros.h"
+
extern "C"
{
// pyerrors.h
#if defined(Py_LIMITED_API) || PY_VERSION_HEX < 0x030C0000
LIBSHIBOKEN_API PyObject *PepErr_GetRaisedException();
+LIBSHIBOKEN_API PyObject *PepException_GetArgs(PyObject *ex);
+LIBSHIBOKEN_API void PepException_SetArgs(PyObject *ex, PyObject *args);
#else
# define PepErr_GetRaisedException PyErr_GetRaisedException
+# define PepException_GetArgs PyException_GetArgs
+# define PepException_SetArgs PyException_SetArgs
#endif
/*****************************************************************************
// needed a debug Python.
//
-// PyUnicode_GetSize is deprecated in favor of PyUnicode_GetLength.
-#define PepUnicode_GetLength(op) PyUnicode_GetLength((PyObject *)(op))
-
// Unfortunately, we cannot ask this at runtime
// #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000
// FIXME: Python 3.10: Replace _PepUnicode_AsString by PyUnicode_AsUTF8
#define Py_TPFLAGS_METHOD_DESCRIPTOR (1UL << 17)
#endif
-extern LIBSHIBOKEN_API int PepRuntime_38_flag;
-
/*****************************************************************************
*
* Runtime support for Python 3.12 incompatibility
{
const Py_ssize_t size = PySequence_Size(pyIn);
for (Py_ssize_t i = 0; i < size; ++i) {
- PyObject *item = PySequence_GetItem(pyIn, i);
+ AutoDecRef item(PySequence_GetItem(pyIn, i));
const bool ok = p(item);
- Py_XDECREF(item);
if (!ok)
return false;
}
{
const Py_ssize_t size = PySequence_Size(pyIn);
for (Py_ssize_t i = 0; i < size; ++i) {
- PyObject *item = PySequence_GetItem(pyIn, i);
+ AutoDecRef item(PySequence_GetItem(pyIn, i));
*out++ = c(item);
- Py_XDECREF(item);
}
}
static inline bool sequenceSizeCheck(PyObject *pyIn, int expectedSize = -1)
{
if (expectedSize >= 0) {
- const int size = int(PySequence_Size(pyIn));
+ const Py_ssize_t size = PySequence_Size(pyIn);
if (size < expectedSize) {
warning(PyExc_RuntimeWarning, 0, "A sequence of size %d was passed to a function that expects %d.",
size, expectedSize);
class Array2Handle
{
public:
- typedef T RowType[columns];
+ using RowType = T[columns];
Array2Handle() = default;
#include "voidptr.h"
#include <string>
+#include <cstring>
+#include <iostream>
#include <unordered_map>
#include <unordered_set>
+#include <map>
+#include <set>
static SbkConverter **PrimitiveTypeConverters;
initArrayConverters();
}
+static void dumpPyTypeObject(std::ostream &str, PyTypeObject *t)
+{
+ str << "\nPython type ";
+ if (t == nullptr) {
+ str << "<None>";
+ return;
+ }
+ str << '"' << t->tp_name << '"';
+ if (t->tp_base != nullptr && t->tp_base != &PyBaseObject_Type)
+ str << '(' << t->tp_base->tp_name << ')';
+}
+
+static void dumpSbkConverter(std::ostream &str, const SbkConverter *c)
+{
+ str << "SbkConverter " << static_cast<const void *>(c) << ": ";
+ if (c->pointerToPython != nullptr)
+ str << ", C++ pointer->Python";
+ if (c->copyToPython != nullptr)
+ str << ", copy->Python";
+ if (c->toCppPointerConversion.second != nullptr)
+ str << ", Python->C++ pointer";
+ if (!c->toCppConversions.empty())
+ str << ", " << c->toCppConversions.size() << " Python->C++ conversions";
+}
+
+// Less than operator for a PyTypeObject for dumping the converter map
+static bool pyTypeObjectLessThan(const PyTypeObject *t1, const PyTypeObject *t2)
+{
+ const bool isNull1 = t1 == nullptr;
+ const bool isNull2 = t2 == nullptr;
+ if (isNull1 || isNull2)
+ return isNull1 && !isNull2;
+ // Internal types (lower case) first
+ const bool isInternal1 = std::islower(t1->tp_name[0]);
+ const bool isInternal2 = std::islower(t2->tp_name[0]);
+ if (isInternal1 != isInternal2)
+ return !isInternal2;
+ return std::strcmp(t1->tp_name, t2->tp_name) < 0;
+}
+
+void dumpConverters()
+{
+ struct PyTypeObjectLess {
+
+ bool operator()(const PyTypeObject *t1, const PyTypeObject *t2) const {
+ return pyTypeObjectLessThan(t1, t2);
+ }
+ };
+
+ using StringSet = std::set<std::string>;
+ using SbkConverterNamesMap = std::unordered_map<SbkConverter *, StringSet>;
+ using PyTypeObjectConverterMap = std::map<PyTypeObject *, SbkConverterNamesMap,
+ PyTypeObjectLess>;
+
+ auto &str = std::cerr;
+
+ // Sort the entries by the associated PyTypeObjects and converters
+ PyTypeObjectConverterMap pyTypeObjectConverterMap;
+ for (const auto &converter : converters) {
+ auto *sbkConverter = converter.second;
+ if (sbkConverter == nullptr) {
+ str << "Non-existent: \"" << converter.first << "\"\n";
+ continue;
+ }
+ auto *typeObject = sbkConverter->pythonType;
+ auto typeIt = pyTypeObjectConverterMap.find(typeObject);
+ if (typeIt == pyTypeObjectConverterMap.end())
+ typeIt = pyTypeObjectConverterMap.insert(std::make_pair(typeObject,
+ SbkConverterNamesMap{})).first;
+ SbkConverterNamesMap &sbkConverterMap = typeIt->second;
+ auto convIt = sbkConverterMap.find(sbkConverter);
+ if (convIt == sbkConverterMap.end())
+ convIt = sbkConverterMap.insert(std::make_pair(sbkConverter,
+ StringSet{})).first;
+ convIt->second.insert(converter.first);
+ }
+
+ for (const auto &tc : pyTypeObjectConverterMap) {
+ dumpPyTypeObject(str, tc.first);
+ str << ", " << tc.second.size() << " converter(s):\n";
+ for (const auto &cn : tc.second) {
+ str << " ";
+ dumpSbkConverter(str, cn.first);
+ str << ", " << cn.second.size() << " alias(es):";
+ int i = 0;
+ for (const auto &name : cn.second) {
+ if ((i++ % 5) == 0)
+ str << "\n ";
+ str << " \"" << name << '"';
+ }
+ str << '\n';
+ }
+ }
+
+ str << '\n';
+}
+
SbkConverter *createConverterObject(PyTypeObject *type,
PythonToCppFunc toCppPointerConvFunc,
IsConvertibleToCppFunc toCppPointerCheckFunc,
CppToPythonFunc pointerToPythonFunc,
CppToPythonFunc copyToPythonFunc)
{
- auto converter = new SbkConverter;
+ auto *converter = new SbkConverter;
converter->pythonType = type;
// PYSIDE-595: All types are heaptypes now, so provide reference.
Py_XINCREF(type);
converter->toCppConversions.push_back(std::make_pair(isConvertibleToCppFunc, pythonToCppFunc));
}
+void prependPythonToCppValueConversion(SbkConverter *converter,
+ PythonToCppFunc pythonToCppFunc,
+ IsConvertibleToCppFunc isConvertibleToCppFunc)
+{
+ converter->toCppConversions.insert(converter->toCppConversions.begin(),
+ std::make_pair(isConvertibleToCppFunc, pythonToCppFunc));
+}
+
void addPythonToCppValueConversion(PyTypeObject *type,
PythonToCppFunc pythonToCppFunc,
IsConvertibleToCppFunc isConvertibleToCppFunc)
}
void registerConverterName(SbkConverter *converter, const char *typeName)
+{
+ auto iter = converters.find(typeName);
+ if (iter == converters.end())
+ converters.insert(std::make_pair(typeName, converter));
+ else
+ iter->second = converter;
+}
+
+void registerConverterAlias(SbkConverter *converter, const char *typeName)
{
auto iter = converters.find(typeName);
if (iter == converters.end())
LIBSHIBOKEN_API void addPythonToCppValueConversion(SbkConverter *converter,
PythonToCppFunc pythonToCppFunc,
IsConvertibleToCppFunc isConvertibleToCppFunc);
+LIBSHIBOKEN_API void prependPythonToCppValueConversion(SbkConverter *converter,
+ PythonToCppFunc pythonToCppFunc,
+ IsConvertibleToCppFunc isConvertibleToCppFunc);
+
LIBSHIBOKEN_API void addPythonToCppValueConversion(PyTypeObject *type,
PythonToCppFunc pythonToCppFunc,
IsConvertibleToCppFunc isConvertibleToCppFunc);
[[deprecated]] LIBSHIBOKEN_API bool isImplicitConversion(PyTypeObject *type, PythonToCppFunc toCpp);
/// Registers a converter with a type name that may be used to retrieve the converter.
+/// Use for fully qualified names (main type). This will overwrite existing converters
+/// of the same name.
LIBSHIBOKEN_API void registerConverterName(SbkConverter *converter, const char *typeName);
+/// Registers a converter with a type name that may be used to retrieve the converter
+/// unless there is already a converter for the name. Use for partially qualified names.
+LIBSHIBOKEN_API void registerConverterAlias(SbkConverter *converter, const char *typeName);
+
/// Returns the converter for a given type name, or NULL if it wasn't registered before.
LIBSHIBOKEN_API SbkConverter *getConverter(const char *typeName);
/// Returns true if the Python type associated with the converter is a wrapper type.
LIBSHIBOKEN_API bool pythonTypeIsWrapperType(const SbkConverter *converter);
-#define SBK_PY_LONG_LONG_IDX 0
+enum : int {
+SBK_PY_LONG_LONG_IDX = 0,
// Qt5: name collision in QtCore after QBool is replaced by bool
-#define SBK_BOOL_IDX_1 1
-#define SBK_CHAR_IDX 2
-#define SBK_CONSTCHARPTR_IDX 3
-#define SBK_DOUBLE_IDX 4
-#define SBK_FLOAT_IDX 5
-#define SBK_INT_IDX 6
-#define SBK_SIGNEDINT_IDX 6
-#define SBK_LONG_IDX 7
-#define SBK_SHORT_IDX 8
-#define SBK_SIGNEDCHAR_IDX 9
-#define SBK_STD_STRING_IDX 10
-#define SBK_STD_WSTRING_IDX 11
-#define SBK_UNSIGNEDPY_LONG_LONG_IDX 12
-#define SBK_UNSIGNEDCHAR_IDX 13
-#define SBK_UNSIGNEDINT_IDX 14
-#define SBK_UNSIGNEDLONG_IDX 15
-#define SBK_UNSIGNEDSHORT_IDX 16
-#define SBK_VOIDPTR_IDX 17
-#define SBK_NULLPTR_T_IDX 18
+SBK_BOOL_IDX_1 = 1,
+SBK_CHAR_IDX = 2,
+SBK_CONSTCHARPTR_IDX = 3,
+SBK_DOUBLE_IDX = 4,
+SBK_FLOAT_IDX = 5,
+SBK_INT_IDX = 6,
+SBK_SIGNEDINT_IDX = 6,
+SBK_LONG_IDX = 7,
+SBK_SHORT_IDX = 8,
+SBK_SIGNEDCHAR_IDX = 9,
+SBK_STD_STRING_IDX = 10,
+SBK_STD_WSTRING_IDX = 11,
+SBK_UNSIGNEDPY_LONG_LONG_IDX = 12,
+SBK_UNSIGNEDCHAR_IDX = 13,
+SBK_UNSIGNEDINT_IDX = 14,
+SBK_UNSIGNEDLONG_IDX = 15,
+SBK_UNSIGNEDSHORT_IDX = 16,
+SBK_VOIDPTR_IDX = 17,
+SBK_NULLPTR_T_IDX = 18
+};
template<typename T> SbkConverter *PrimitiveTypeConverter() { return nullptr; }
template<> inline SbkConverter *PrimitiveTypeConverter<PY_LONG_LONG>() { return primitiveTypeConverter(SBK_PY_LONG_LONG_IDX); }
}
static void toCpp(PyObject *pyIn, void *cppOut)
{
- double result = PyFloat_AS_DOUBLE(pyIn);
+ double result = PyFloat_AsDouble(pyIn);
// If cast to long directly it could overflow silently.
if (OverFlowChecker<INT>::check(result, pyIn))
PyErr_SetObject(PyExc_OverflowError, nullptr);
CppToPythonFunc pointerToPythonFunc,
CppToPythonFunc copyToPythonFunc);
+LIBSHIBOKEN_API void dumpConverters();
+
/// Interface for sbkmodule which must reset cache when new module is loaded.
LIBSHIBOKEN_API void clearNegativeLazyCache();
}
if (PyBytes_Check(str))
- value->assign(PyBytes_AS_STRING(str));
+ value->assign(PyBytes_AsString(str));
}
void toCppWString(PyObject *str, std::wstring *value)
#include "sbkconverter.h"
#include "basewrapper.h"
#include "autodecref.h"
-#include "sbkpython.h"
-#include "signature.h"
+#include "sbktypefactory.h"
#include <cstring>
#include <vector>
if (metatype != reinterpret_cast<PyTypeObject *>(PyEnumMeta))
return -1;
auto *mro = reinterpret_cast<PyTypeObject *>(ob_type)->tp_mro;
- const Py_ssize_t n = PyTuple_GET_SIZE(mro);
+ const Py_ssize_t n = PyTuple_Size(mro);
for (Py_ssize_t idx = 0; idx < n; ++idx) {
- auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, idx));
+ auto *sub_type = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(mro, idx));
if (sub_type == reinterpret_cast<PyTypeObject *>(PyFlag))
return 1;
}
static auto *const _mro = Shiboken::String::createStaticString("__mro__");
static auto *const _class = Shiboken::String::createStaticString("__class__");
- PyObject *klass{}, *value{};
+ PyObject *klass{};
+ PyObject *value{};
if (!PyArg_UnpackTuple(args, "missing", 2, 2, &klass, &value))
Py_RETURN_NONE;
if (!PyLong_Check(value))
int enumOption{};
bool check(PyObject *pyObj)
+{
+ return checkType(Py_TYPE(pyObj));
+}
+
+bool checkType(PyTypeObject *pyTypeObj)
{
init_enum();
static PyTypeObject *meta = getPyEnumMeta();
- return Py_TYPE(Py_TYPE(pyObj)) == reinterpret_cast<PyTypeObject *>(meta);
+ return Py_TYPE(pyTypeObj) == reinterpret_cast<PyTypeObject *>(meta);
}
PyObject *getEnumItemFromValue(PyTypeObject *enumType, EnumValueType itemValue)
return PyLong_AsLongLong(pyValue);
}
-void setTypeConverter(PyTypeObject *type, SbkConverter *converter)
+void setTypeConverter(PyTypeObject *type, SbkConverter *converter,
+ SbkConverter *flagsConverter)
{
- auto *enumType = reinterpret_cast<SbkEnumType *>(type);
- PepType_SETP(enumType)->converter = converter;
+ SbkEnumTypePrivate *priv = PepType_SETP(reinterpret_cast<SbkEnumType *>(type));
+ priv->converter = converter;
+ priv->flagsConverter = flagsConverter;
}
static PyTypeObject *createEnumForPython(PyObject *scopeOrModule,
auto *key = PyUnicode_FromString(kv);
auto *value = toPyObject(enumValues[idx]);
auto *key_value = PyTuple_New(2);
- PyTuple_SET_ITEM(key_value, 0, key);
- PyTuple_SET_ITEM(key_value, 1, value);
+ PyTuple_SetItem(key_value, 0, key);
+ PyTuple_SetItem(key_value, 1, value);
PyList_Append(pyEnumItems, key_value);
}
return createEnumForPython(module, fullName, pyEnumItems);
struct SbkEnumTypePrivate
{
SbkConverter *converter;
+ SbkConverter *flagsConverter;
};
/// PYSIDE-1735: Pass on the Python enum/flag information.
using EnumValueType = long long;
LIBSHIBOKEN_API bool check(PyObject *obj);
+LIBSHIBOKEN_API bool checkType(PyTypeObject *pyTypeObj);
LIBSHIBOKEN_API PyObject *newItem(PyTypeObject *enumType, EnumValueType itemValue,
const char *itemName = nullptr);
EnumValueType itemValue);
/// Sets the enum/flag's type converter.
-LIBSHIBOKEN_API void setTypeConverter(PyTypeObject *type, SbkConverter *converter);
+LIBSHIBOKEN_API void setTypeConverter(PyTypeObject *type, SbkConverter *converter,
+ SbkConverter *flagsConverter = nullptr);
/// Creating Python enums for different types.
LIBSHIBOKEN_API PyTypeObject *createPythonEnum(PyObject *module,
#include "helper.h"
#include "gilstate.h"
+#include <cstdio>
+#include <string>
+
+using namespace std::literals::string_literals;
+
namespace Shiboken
{
namespace Errors
{
+void setIndexOutOfBounds(Py_ssize_t value, Py_ssize_t minValue, Py_ssize_t maxValue)
+{
+ PyErr_Format(PyExc_IndexError,
+ "index %zd out of bounds %zd..%zd", value, minValue, maxValue);
+}
+
void setInstantiateAbstractClass(const char *name)
{
PyErr_Format(PyExc_NotImplementedError,
PyErr_SetString(PyExc_TypeError, "Wrong type passed to container conversion.");
}
+// Prepend something to an exception message provided it is a single string
+// argument.
+static bool prependToExceptionMessage(PyObject *exc, const char *context)
+{
+ Shiboken::AutoDecRef args(PepException_GetArgs(exc));
+ if (args.isNull() || PyTuple_Check(args.object()) == 0 || PyTuple_Size(args) != 1)
+ return false;
+ auto *oldMessage = PyTuple_GetItem(args, 0);
+ if (oldMessage == nullptr || PyUnicode_CheckExact(oldMessage) == 0)
+ return false;
+ auto *newMessage = PyUnicode_FromFormat("%s%U", context, oldMessage);
+ PepException_SetArgs(exc, PyTuple_Pack(1, newMessage));
+ return true;
+}
+
struct ErrorStore {
PyObject *type;
PyObject *exc;
static thread_local ErrorStore savedError{};
+static bool hasPythonContext()
+{
+ return _pythonContextStack & 1;
+}
+
void storeErrorOrPrint()
{
// This error happened in a function with no way to return an error state.
// Therefore, we handle the error when we are error checking, anyway.
// But we do that only when we know that an error handler can pick it up.
- if (_pythonContextStack & 1)
+ if (hasPythonContext())
PyErr_Fetch(&savedError.type, &savedError.exc, &savedError.traceback);
else
PyErr_Print();
}
+// Like storeErrorOrPrint() with additional context info that is prepended
+// to the exception message or printed.
+static void storeErrorOrPrintWithContext(const char *context)
+{
+ if (hasPythonContext()) {
+ PyErr_Fetch(&savedError.type, &savedError.exc, &savedError.traceback);
+ prependToExceptionMessage(savedError.exc, context);
+ } else {
+ std::fputs(context, stderr);
+ PyErr_Print();
+ }
+}
+
+void storePythonOverrideErrorOrPrint(const char *className, const char *funcName)
+{
+ const std::string context = "Error calling Python override of "s
+ + className + "::"s + funcName + "(): "s;
+ storeErrorOrPrintWithContext(context.c_str());
+}
+
PyObject *occurred()
{
if (savedError.type) {
namespace Errors
{
+LIBSHIBOKEN_API void setIndexOutOfBounds(Py_ssize_t value, Py_ssize_t minValue,
+ Py_ssize_t maxValue);
LIBSHIBOKEN_API void setInstantiateAbstractClass(const char *name);
LIBSHIBOKEN_API void setInstantiateAbstractClassDisabledWrapper(const char *name);
LIBSHIBOKEN_API void setInvalidTypeDeletion(const char *name);
/// This replaces `PyErr_Print`, which cannot report errors as exception.
/// To be used in contexts where raising errors is impossible.
LIBSHIBOKEN_API void storeErrorOrPrint();
+
+/// Call storeErrorOrPrint() and print the context to report
+/// errors when calling Python overrides of virtual functions.
+LIBSHIBOKEN_API void storePythonOverrideErrorOrPrint(const char *className, const char *funcName);
+
/// Handle an error as in PyErr_Occurred(), but also check for errors which
/// were captured by `storeErrorOrPrint`.
/// To be used in normal error checks.
PyErr_Restore(error_type, error_value, error_traceback);
}
-// python 3.12
-static int const CALL = 171;
+// Python 3.13
+static int const LOAD_ATTR_313 = 82;
+static int const CALL_313 = 53;
+static int const PUSH_NULL_313 = 34;
+// Python 3.12
+static int const CALL_312 = 171;
// Python 3.11
static int const PRECALL = 166;
// we have "big instructions" with gaps after them
static int const CALL_METHOD = 161;
// Python 3.6
static int const CALL_FUNCTION = 131;
-static int const LOAD_ATTR = 106;
+static int const LOAD_ATTR_312 = 106;
// NoGil (how long will this exist in this form?)
static int const LOAD_METHOD_NOGIL = 55;
static int const CALL_METHOD_NOGIL = 72;
static bool currentOpcode_Is_CallMethNoArgs()
{
+ static auto number = _PepRuntimeVersion();
+ static int LOAD_ATTR = number < 0x030D00 ? LOAD_ATTR_312 : LOAD_ATTR_313;
+ static int CALL = number < 0x030D00 ? CALL_312 : CALL_313;
// PYSIDE-2221: Special case for the NoGil version:
// Find out if we have such a version.
// We could also ask the variable `Py_NOGIL`.
// We look into the currently active operation if we are going to call
// a method with zero arguments.
auto *frame = PyEval_GetFrame();
+ if (frame == nullptr) // PYSIDE-2796, has been observed to fail
+ return false;
#if !Py_LIMITED_API && !defined(PYPY_VERSION)
auto *f_code = PyFrame_GetCode(frame);
#else
}
uint8_t opcode2 = co_code[f_lasti + 2];
uint8_t oparg2 = co_code[f_lasti + 3];
- static auto number = _PepRuntimeVersion();
if (number < 0x030B00)
return opcode1 == LOAD_METHOD && opcode2 == CALL_METHOD && oparg2 == 0;
// don't need to take care of them.
if (opcode1 == LOAD_METHOD)
f_lasti += LOAD_METHOD_GAP_311;
- else if (opcode1 == LOAD_ATTR)
+ else if (opcode1 == LOAD_ATTR_312)
f_lasti += LOAD_ATTR_GAP_311;
else
return false;
else
return false;
+ if (number >= 0x030D00) {
+ int opcode3 = co_code[f_lasti + 2];
+ if (opcode3 == PUSH_NULL_313)
+ f_lasti += 2;
+ }
opcode2 = co_code[f_lasti + 2];
oparg2 = co_code[f_lasti + 3];
PyObject *mro = type->tp_mro;
PyObject *result{};
assert(PyTuple_Check(mro));
- Py_ssize_t idx, n = PyTuple_GET_SIZE(mro);
- for (idx = 0; idx < n; ++idx) {
- auto *base = PyTuple_GET_ITEM(mro, idx);
+ for (Py_ssize_t idx = 0, n = PyTuple_Size(mro); idx < n; ++idx) {
+ auto *base = PyTuple_GetItem(mro, idx);
auto *type_base = reinterpret_cast<PyTypeObject *>(base);
if (!SbkObjectType_Check(type_base))
continue;
#include <unordered_map>
#include <unordered_set>
+#include <vector>
#include <cstring>
+/// This hash maps module objects to arrays of converters.
+using ModuleConvertersMap = std::unordered_map<PyObject *, SbkConverter **> ;
+
/// This hash maps module objects to arrays of Python types.
using ModuleTypesMap = std::unordered_map<PyObject *, Shiboken::Module::TypeInitStruct *> ;
-/// This hash maps module objects to arrays of converters.
-using ModuleConvertersMap = std::unordered_map<PyObject *, SbkConverter **>;
+struct TypeCreationStruct
+{
+ Shiboken::Module::TypeCreationFunction func;
+ std::vector<std::string> subtypeNames;
+};
-/// This hash maps type names to type creation functions.
-using TypeCreationFunctionModulePair =
- std::pair<Shiboken::Module::TypeCreationFunction, PyObject *>;
-using NameToTypeFunctionMap = std::unordered_map<std::string, TypeCreationFunctionModulePair>;
+/// This hash maps type names to type creation structs.
+using NameToTypeFunctionMap = std::unordered_map<std::string, TypeCreationStruct> ;
/// This hash maps module objects to maps of names to functions.
using ModuleToFuncsMap = std::unordered_map<PyObject *, NameToTypeFunctionMap> ;
return typeStruct.type;
}
+static void incarnateHelper(PyObject *module, const std::string_view names,
+ const NameToTypeFunctionMap &nameToFunc)
+{
+ auto dotPos = names.find('.');
+ std::string::size_type startPos = 0;
+ auto *modOrType{module};
+ while (dotPos != std::string::npos) {
+ auto typeName = names.substr(startPos, dotPos - startPos);
+ AutoDecRef obTypeName(String::fromCppStringView(typeName));
+ modOrType = PyObject_GetAttr(modOrType, obTypeName);
+ startPos = dotPos + 1;
+ dotPos = names.find('.', startPos);
+ }
+ // now we have the type to create. (May be done already)
+ auto funcIter = nameToFunc.find(std::string(names));
+ if (funcIter == nameToFunc.end())
+ return;
+ // - call this function that returns a PyTypeObject
+ auto tcStruct = funcIter->second;
+ auto initFunc = tcStruct.func;
+ PyTypeObject *type = initFunc(modOrType);
+ auto name = names.substr(startPos);
+ PyObject_SetAttrString(modOrType, name.data(), reinterpret_cast<PyObject *>(type));
+}
+
+static void incarnateSubtypes(PyObject *module,
+ const std::vector<std::string> &nameList,
+ NameToTypeFunctionMap &nameToFunc)
+{
+ for (auto const & tableIter : nameList) {
+ std::string_view names(tableIter);
+ incarnateHelper(module, names, nameToFunc);
+ }
+}
+
static PyTypeObject *incarnateType(PyObject *module, const char *name,
NameToTypeFunctionMap &nameToFunc)
{
return nullptr;
}
// - call this function that returns a PyTypeObject
- auto pair = funcIter->second;
- auto initFunc = pair.first;
- auto *modOrType = pair.second;
+ auto tcStruct = funcIter->second;
+ auto initFunc = tcStruct.func;
+ auto *modOrType{module};
// PYSIDE-2404: Make sure that no switching happens during type creation.
auto saveFeature = initSelectableFeature(nullptr);
PyTypeObject *type = initFunc(modOrType);
+ if (!tcStruct.subtypeNames.empty())
+ incarnateSubtypes(module, tcStruct.subtypeNames, nameToFunc);
initSelectableFeature(saveFeature);
// - assign this object to the name in the module
// PYSIDE-2404: Completely load all not yet loaded classes.
// This is needed to resolve a star import.
+// PYSIDE-2898: Use a name list to pick the toplevel types.
void resolveLazyClasses(PyObject *module)
{
// - locate the module in the moduleTofuncs mapping
// - see if there are still unloaded elements
auto &nameToFunc = tableIter->second;
- // - incarnate all types.
- while (!nameToFunc.empty()) {
- auto it = nameToFunc.begin();
- auto attrNameStr = it->first;
- incarnateType(module, attrNameStr.c_str(), nameToFunc);
+ // - keep a filtered list of names without the subtypes
+ std::vector<std::string> names{};
+ names.reserve(nameToFunc.size());
+ for (const auto &funcIter : nameToFunc) {
+ if (funcIter.first.find('.') == std::string::npos)
+ names.push_back(funcIter.first);
+ }
+
+ // - incarnate all toplevel types. Subtypes are handled there.
+ for (const auto &nameIter : names) {
+ auto funcIter = nameToFunc.find(nameIter);
+ if (funcIter != nameToFunc.end())
+ incarnateType(module, nameIter.c_str(), nameToFunc);
}
}
// - check if the attribute is present and return it.
auto *attr = PyObject_GenericGetAttr(module, name);
// - we handle AttributeError, only.
- if (!(attr == nullptr && PyErr_ExceptionMatches(PyExc_AttributeError)))
+ if (attr != nullptr || PyErr_ExceptionMatches(PyExc_AttributeError) == 0)
return attr;
PyErr_Clear();
// - locate the name and retrieve the generating function
const char *attrNameStr = Shiboken::String::toCString(name);
auto &nameToFunc = tableIter->second;
- // - create the real type (incarnateType checks this)
+ // - create the real type and handle subtypes
auto *type = incarnateType(module, attrNameStr, nameToFunc);
auto *ret = reinterpret_cast<PyObject *>(type);
// - if attribute does really not exist use the original
PyErr_Clear();
return origModuleGetattro(module, name);
}
-
return ret;
}
// Python 3.8 - 3.12
static int const LOAD_CONST_312 = 100;
static int const IMPORT_NAME_312 = 108;
+// Python 3.13
+static int const LOAD_CONST_313 = 83;
+static int const IMPORT_NAME_313 = 75;
static bool isImportStar(PyObject *module)
{
static PyObject *const _co_consts = Shiboken::String::createStaticString("co_consts");
static PyObject *const _co_names = Shiboken::String::createStaticString("co_names");
+ static int LOAD_CONST = _PepRuntimeVersion() < 0x030D00 ? LOAD_CONST_312 : LOAD_CONST_313;
+ static int IMPORT_NAME = _PepRuntimeVersion() < 0x030D00 ? IMPORT_NAME_312 : IMPORT_NAME_313;
+
auto *obFrame = reinterpret_cast<PyObject *>(PyEval_GetFrame());
if (obFrame == nullptr)
return true; // better assume worst-case.
AutoDecRef dec_co_code(PyObject_GetAttr(dec_f_code, _co_code));
AutoDecRef dec_f_lasti(PyObject_GetAttr(dec_frame, _f_lasti));
Py_ssize_t f_lasti = PyLong_AsSsize_t(dec_f_lasti);
- Py_ssize_t code_len;
+ Py_ssize_t code_len{};
char *co_code{};
PyBytes_AsStringAndSize(dec_co_code, &co_code, &code_len);
uint8_t opcode2 = co_code[f_lasti];
uint8_t opcode1 = co_code[f_lasti - 2];
- if (opcode1 == LOAD_CONST_312 && opcode2 == IMPORT_NAME_312) {
+ if (opcode1 == LOAD_CONST && opcode2 == IMPORT_NAME) {
uint8_t oparg1 = co_code[f_lasti - 1];
uint8_t oparg2 = co_code[f_lasti + 1];
AutoDecRef dec_co_consts(PyObject_GetAttr(dec_f_code, _co_consts));
// PYSIDE-2404: These modules produce ambiguous names which we cannot handle, yet.
static std::unordered_set<std::string> dontLazyLoad{
- "sample",
- "smart",
"testbinding"
};
return result;
}
-void AddTypeCreationFunction(PyObject *module,
- const char *name,
- TypeCreationFunction func)
+void checkIfShouldLoadImmediately(PyObject *module, const std::string &name,
+ const NameToTypeFunctionMap &nameToFunc)
{
static const int value = lazyLoadDefault();
- // - locate the module in the moduleTofuncs mapping
- auto tableIter = moduleToFuncs.find(module);
- assert(tableIter != moduleToFuncs.end());
- // - Assign the name/generating function pair.
- auto &nameToFunc = tableIter->second;
- TypeCreationFunctionModulePair pair{func, module};
- auto nit = nameToFunc.find(name);
- if (nit == nameToFunc.end())
- nameToFunc.insert(std::make_pair(name, pair));
- else
- nit->second = pair;
-
// PYSIDE-2404: Lazy Loading
//
// Options:
// 3 - lazy loading for any module.
//
// By default we lazy load all known modules (option = 1).
-
if (value == 0 // completely disabled
|| canNotLazyLoad(module) // for some reason we cannot lazy load
|| (value == 1 && !shouldLazyLoad(module)) // not a known module
) {
- PyTypeObject *type = func(module);
- PyModule_AddObject(module, name, reinterpret_cast<PyObject *>(type)); // steals reference
+ incarnateHelper(module, name, nameToFunc);
}
}
void AddTypeCreationFunction(PyObject *module,
const char *name,
- TypeCreationFunction func,
- const char *containerName)
+ TypeCreationFunction func)
{
- // This version could be delayed as well, but for the few cases
- // we simply fetch the container type and insert directly.
- AutoDecRef obContainerType(PyObject_GetAttrString(module, containerName));
- PyTypeObject *type = func(obContainerType);
- PyObject_SetAttrString(obContainerType, name, reinterpret_cast<PyObject *>(type)); // steals reference
-}
+ // - locate the module in the moduleTofuncs mapping
+ auto tableIter = moduleToFuncs.find(module);
+ assert(tableIter != moduleToFuncs.end());
+ // - Assign the name/generating function tcStruct.
+ auto &nameToFunc = tableIter->second;
+ TypeCreationStruct tcStruct{func, {}};
+ auto nit = nameToFunc.find(name);
+ if (nit == nameToFunc.end())
+ nameToFunc.insert(std::make_pair(name, tcStruct));
+ else
+ nit->second = tcStruct;
-void AddTypeCreationFunction(PyObject *module,
- const char *name,
- TypeCreationFunction func,
- const char *outerContainerName,
- const char *innerContainerName)
-{
- // This version has even more indirection. It is very rare, and
- // we handle it directly.
- AutoDecRef obOuterType(PyObject_GetAttrString(module, outerContainerName));
- AutoDecRef obInnerType(PyObject_GetAttrString(obOuterType, innerContainerName));
- PyTypeObject *type = func(obInnerType);
- PyObject_SetAttrString(obInnerType, name, reinterpret_cast<PyObject *>(type)); // steals reference
+ checkIfShouldLoadImmediately(module, name, nameToFunc);
}
void AddTypeCreationFunction(PyObject *module,
- const char *name,
+ const char *containerName,
TypeCreationFunction func,
- const char *containerName3,
- const char *containerName2,
- const char *containerName)
+ const char *namePath)
{
- // This version has even mode indirection. It is very rare, and
- // we handle it directly.
- AutoDecRef obContainerType3(PyObject_GetAttrString(module, containerName3));
- AutoDecRef obContainerType2(PyObject_GetAttrString(obContainerType3, containerName2));
- AutoDecRef obContainerType(PyObject_GetAttrString(obContainerType2, containerName));
- PyTypeObject *type = func(obContainerType);
- PyObject_SetAttrString(obContainerType, name, reinterpret_cast<PyObject *>(type)); // steals reference
+ // - locate the module in the moduleTofuncs mapping
+ auto tableIter = moduleToFuncs.find(module);
+ assert(tableIter != moduleToFuncs.end());
+ // - Assign the name/generating function tcStruct.
+ auto &nameToFunc = tableIter->second;
+ auto nit = nameToFunc.find(containerName);
+
+ // - insert namePath into the subtype vector of the main type.
+ nit->second.subtypeNames.emplace_back(namePath);
+ // - insert it also as its own entry.
+ nit = nameToFunc.find(namePath);
+ TypeCreationStruct tcStruct{func, {}};
+ if (nit == nameToFunc.end())
+ nameToFunc.insert(std::make_pair(namePath, tcStruct));
+ else
+ nit->second = tcStruct;
+
+ checkIfShouldLoadImmediately(module, namePath, nameToFunc);
}
PyObject *import(const char *moduleName)
Py_INCREF(origImportFunc);
AutoDecRef func(PyCFunction_NewEx(lazy_methods, nullptr, nullptr));
PyDict_SetItemString(builtins, "__import__", func);
- // Everything is set.
lazy_init = true;
}
// PYSIDE-2404: Nuitka inserts some additional code in standalone mode
return (iter == moduleConverters.end()) ? 0 : iter->second;
}
+// Replace the dictionary of a module by a different dict.
+// The dict should be filled with the content of the old dict, before.
+// Reason: Creating a module dict with __missing__ support.
+typedef struct {
+ PyObject_HEAD
+ PyObject *md_dict;
+} StartOf_PyModuleObject;
+
+bool replaceModuleDict(PyObject *module, PyObject *modClass, PyObject *dict)
+{
+ if (!(PyModule_Check(module) && PyType_Check(modClass) && PyDict_Check(dict)))
+ return false;
+ auto *modict = PyModule_GetDict(module);
+ auto *modIntern = reinterpret_cast<StartOf_PyModuleObject *>(module);
+ if (modict != modIntern->md_dict)
+ Py_FatalError("The layout of modules is incompatible");
+ auto *hold = modIntern->md_dict;
+ modIntern->md_dict = dict;
+ Py_INCREF(dict);
+ Py_DECREF(hold);
+ Py_INCREF(modClass);
+ module->ob_type = reinterpret_cast<PyTypeObject *>(modClass);
+ return true;
+}
+
} } // namespace Shiboken::Module
TypeCreationFunction func,
const char *containerName);
-LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module,
- const char *name,
- TypeCreationFunction func,
- const char *outerContainerName,
- const char *innerContainerName);
-
-LIBSHIBOKEN_API void AddTypeCreationFunction(PyObject *module,
- const char *name,
- TypeCreationFunction func,
- const char *containerName3,
- const char *containerName2,
- const char *containerName);
/**
* Registers the list of types created by \p module.
* \param module Module where the types were created.
*/
LIBSHIBOKEN_API SbkConverter **getTypeConverters(PyObject *module);
+/**
+ * Replace the dictionary of a module. This allows to use `__missing__`.
+ */
+LIBSHIBOKEN_API bool replaceModuleDict(PyObject *module, PyObject *modClass, PyObject *dict);
+
} // namespace Shiboken::Module
#endif // SBK_MODULE_H
#include "sbkversion.h"
+// PYSIDE-2701: This definition is needed for all Python formats with "#".
+#define PY_SSIZE_T_CLEAN
+
// Qt's "slots" macro collides with the "slots" member variables
// used in some Python structs. For compilers that support push_macro,
// temporarily undefine it.
#if defined(slots) && (defined(__GNUC__) || defined(_MSC_VER) || defined(__clang__))
# pragma push_macro("slots")
# undef slots
-/*
- * Python 2 has function _Py_Mangle directly in Python.h .
- * This creates wrong language binding unless we define 'extern "C"' here.
- */
-extern "C" {
-/*
- * Python 2 uses the "register" keyword, which is deprecated in C++ 11
- * and forbidden in C++17.
- */
-# if defined(__clang__)
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wdeprecated-register"
-# endif
+extern "C" {
# include <Python.h>
-
-# if defined(__clang__)
-# pragma clang diagnostic pop
-# endif
}
+
# include <structmember.h>
// Now we have the usual variables from Python.h .
# include "shibokenmacros.h"
#else
extern "C" {
-/*
- * Python 2 uses the "register" keyword, which is deprecated in C++ 11
- * and forbidden in C++17.
- */
-# if defined(__clang__)
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wdeprecated-register"
-# endif
-
# include <Python.h>
-
-# if defined(__clang__)
-# pragma clang diagnostic pop
-# endif
}
+
# include <structmember.h>
// Now we have the usual variables from Python.h .
# include "shibokenmacros.h"
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "sbkstring.h"
+#include "sbkenum.h"
#include "sbkstaticstrings_p.h"
#include "autodecref.h"
return PyObject_HasAttr(obj, Shiboken::PyMagicName::iter());
}
+bool checkIterableArgument(PyObject *obj)
+{
+ return checkIterable(obj) && !Shiboken::Enum::check(obj);
+}
+
static PyObject *initPathLike()
{
PyObject *PathLike{};
if (PyUnicode_Check(str))
return _PepUnicode_AsString(str);
if (PyBytes_Check(str))
- return PyBytes_AS_STRING(str);
+ return PyBytes_AsString(str);
return nullptr;
}
if (PyUnicode_Check(str)) {
// We need to encode the unicode string into utf8 to know the size of returned char *.
Shiboken::AutoDecRef uniStr(PyUnicode_AsUTF8String(str));
- *len = PyBytes_GET_SIZE(uniStr.object());
+ *len = PyBytes_Size(uniStr.object());
// Return unicode from str instead of uniStr, because the lifetime of the returned pointer
// depends on the lifetime of str.
return _PepUnicode_AsString(str);
}
if (PyBytes_Check(str)) {
- *len = PyBytes_GET_SIZE(str);
- return PyBytes_AS_STRING(str);
+ *len = PyBytes_Size(str);
+ return PyBytes_AsString(str);
}
return nullptr;
}
return 0;
if (PyUnicode_Check(str))
- return PepUnicode_GetLength(str);
+ return PyUnicode_GetLength(str);
if (PyBytes_Check(str))
- return PyBytes_GET_SIZE(str);
+ return PyBytes_Size(str);
return 0;
}
#include "sbkpython.h"
#include "shibokenmacros.h"
-namespace Shiboken
-{
-namespace String
+namespace Shiboken::String
{
LIBSHIBOKEN_API bool check(PyObject *obj);
LIBSHIBOKEN_API bool checkIterable(PyObject *obj);
+ /// Check for iterable function arguments (excluding enumerations)
+ LIBSHIBOKEN_API bool checkIterableArgument(PyObject *obj);
LIBSHIBOKEN_API bool checkPath(PyObject *path);
LIBSHIBOKEN_API bool checkType(PyTypeObject *obj);
LIBSHIBOKEN_API bool checkChar(PyObject *obj);
LIBSHIBOKEN_API PyObject *getSnakeCaseName(PyObject *name, bool lower);
LIBSHIBOKEN_API PyObject *repr(PyObject *o);
-} // namespace String
-} // namespace Shiboken
-
+} // namespace Shiboken::String
#endif
-
-
bases = basesPatch.object();
}
- Py_ssize_t n = PyTuple_GET_SIZE(bases);
- for (auto idx = 0; idx < n; ++idx) {
- PyTypeObject *base = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(bases, idx));
+ for (Py_ssize_t idx = 0, n = PyTuple_Size(bases); idx < n; ++idx) {
+ PyTypeObject *base = reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(bases, idx));
PyTypeObject *meta = Py_TYPE(base);
if (meta->tp_new != PyType_Type.tp_new) {
// make sure there is no second meta class
keepMeta->tp_new = keepNew;
if (basesPatch.object()) {
// undo the metaclass patch.
- auto *base = PyTuple_GET_ITEM(basesPatch.object(), 0);
+ auto *base = PyTuple_GetItem(basesPatch.object(), 0);
base->ob_type = &PyType_Type;
}
return ret;
best_base(PyObject *bases)
{
// We always have only one base
- return reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(bases, 0));
+ return reinterpret_cast<PyTypeObject *>(PyTuple_GetItem(bases, 0));
}
static PyObject *
#include "sbkmodule.h"
#include "sbkstring.h"
#include "sbkstaticstrings.h"
+#include "sbktypefactory.h"
#include "shibokenmacros.h"
#include "shibokenbuffer.h"
#include "signature.h"
#endif // SHIBOKEN_H
-
#include "sbkpython.h"
#include "shibokenmacros.h"
-namespace Shiboken
-{
-
-namespace Buffer
+namespace Shiboken::Buffer
{
enum Type {
ReadOnly,
*/
LIBSHIBOKEN_API void *copyData(PyObject *pyObj, Py_ssize_t *size = nullptr);
-} // namespace Buffer
-} // namespace Shiboken
+} // namespace Shiboken::Buffer
#endif
{
LIBSHIBOKEN_API int InitSignatureStrings(PyTypeObject *, const char *[]);
-LIBSHIBOKEN_API void FinishSignatureInitialization(PyObject *, const char *[]);
+LIBSHIBOKEN_API int InitSignatureBytes(PyTypeObject *, const uint8_t[], size_t);
+LIBSHIBOKEN_API int FinishSignatureInitialization(PyObject *, const char *[]);
+LIBSHIBOKEN_API int FinishSignatureInitBytes(PyObject *, const uint8_t [], size_t);
LIBSHIBOKEN_API void SetError_Argument(PyObject *, const char *, PyObject *);
LIBSHIBOKEN_API PyObject *Sbk_TypeGet___doc__(PyObject *);
LIBSHIBOKEN_API PyObject *GetFeatureDict();
#include <structmember.h>
+#include <algorithm>
+
using namespace Shiboken;
extern "C"
PyObject *ret = get_signature_intern(ob, modifier);
if (ret != nullptr)
return ret;
+ if (PyErr_Occurred())
+ return nullptr;
Py_RETURN_NONE;
}
+////////////////////////////////////////////////////////////////////////////
+//
+// make_snake_case_name -- make efficient name change available in Python
+//
+//
+
+static PyObject *make_snake_case_name(PyObject * /* self */, PyObject *arg)
+{
+ if (!PyUnicode_Check(arg))
+ return PyErr_Format(PyExc_TypeError, "%S is not a string");
+ return Shiboken::String::getSnakeCaseName(arg, true);
+}
+
////////////////////////////////////////////////////////////////////////////
//
// feature_import -- special handling for `from __feature__ import ...`
}
PyMethodDef signature_methods[] = {
- {"__feature_import__", (PyCFunction)feature_import, METH_VARARGS | METH_KEYWORDS, nullptr},
- {"get_signature", (PyCFunction)get_signature, METH_VARARGS,
+ {"__feature_import__", reinterpret_cast<PyCFunction>(feature_import),
+ METH_VARARGS | METH_KEYWORDS, nullptr},
+ {"get_signature", reinterpret_cast<PyCFunction>(get_signature), METH_VARARGS,
"get the signature, passing an optional string parameter"},
+ {"make_snake_case_name", reinterpret_cast<PyCFunction>(make_snake_case_name), METH_O,
+ "turn a camelCase name into snake_case"},
{nullptr, nullptr, 0, nullptr}
};
* address of a string array. It will not be turned into a real
* string list until really used by Python. This is quite optimal.
*/
- AutoDecRef numkey(Py_BuildValue("n", signatures));
+ AutoDecRef numkey(PyLong_FromVoidPtr(signatures));
if (type_key.isNull() || numkey.isNull()
|| PyDict_SetItem(pyside_globals->arg_dict, type_key, numkey) < 0)
return -1;
return PyDict_SetItem(pyside_globals->map_dict, type_key, obtype_mod) == 0 ? 0 : -1;
}
+static int PySide_BuildSignatureArgsByte(PyObject *obtype_mod, const uint8_t *signatures,
+ size_t size)
+{
+ AutoDecRef type_key(GetTypeKey(obtype_mod));
+ AutoDecRef numkey(PyTuple_New(2));
+ PyTuple_SetItem(numkey.object(), 0, PyLong_FromVoidPtr(const_cast<uint8_t *>(signatures)));
+ PyTuple_SetItem(numkey.object(), 1, PyLong_FromSize_t(size));
+ if (type_key.isNull() || numkey.isNull()
+ || PyDict_SetItem(pyside_globals->arg_dict, type_key, numkey) < 0)
+ return -1;
+ return PyDict_SetItem(pyside_globals->map_dict, type_key, obtype_mod) == 0 ? 0 : -1;
+}
+
+// PYSIDE-2701: MS cannot use the name "_expand".
+static PyObject *byteExpand(PyObject *packed)
+{
+ const char commonMsg[] = "Please disable compression by passing --unoptimize=compression";
+
+ static PyObject *compressModule = PyImport_ImportModule("zlib");
+ if (compressModule == nullptr)
+ return PyErr_Format(PyExc_ImportError,
+ "The zlib module cannot be imported. %s", commonMsg);
+
+ static PyObject *expandFunc = PyObject_GetAttrString(compressModule, "decompress");
+ if (expandFunc == nullptr)
+ return PyErr_Format(PyExc_NameError,
+ "The expand function of zlib was not fount. %s", commonMsg);
+ PyObject *unpacked = PyObject_CallFunctionObjArgs(expandFunc, packed, nullptr);
+ if (unpacked == nullptr)
+ return PyErr_Format(PyExc_ValueError,
+ "Some packed strings could not be unpacked. %s", commonMsg);
+ return unpacked;
+}
+
+const char **bytesToStrings(const uint8_t signatures[], Py_ssize_t size)
+{
+ // PYSIDE-2701: Unpack a ZLIB compressed string.
+ // The result is a single char* with newlines after each string. Convert
+ // this into single char* objects that InitSignatureStrings expects.
+
+ const auto *chars = reinterpret_cast<const char *>(signatures);
+ PyObject *packed = PyBytes_FromStringAndSize(chars, size);
+
+ // The Qt compressor treats empty arrays specially.
+ PyObject *data = size > 0 ? byteExpand(packed) : PyBytes_FromStringAndSize(chars, 0);
+ if (data == nullptr)
+ return nullptr;
+
+ char *cdata{};
+ Py_ssize_t len{};
+ PyBytes_AsStringAndSize(data, &cdata, &len);
+ char *cdataEnd = cdata + len;
+ size_t nlines = std::count(cdata, cdataEnd, '\n');
+
+ char **lines = new char *[nlines + 1];
+ int pos = 0;
+ char *line = cdata;
+
+ for (char *p = cdata; p < cdataEnd; ++p) {
+ if (*p == '\n') {
+ lines[pos++] = line;
+ *p = 0;
+ line = p + 1;
+ }
+ }
+ lines[pos] = nullptr;
+ return const_cast<const char **>(lines);
+}
+
PyObject *PySide_BuildSignatureProps(PyObject *type_key)
{
/*
*/
if (type_key == nullptr)
return nullptr;
+ AutoDecRef strings{};
PyObject *numkey = PyDict_GetItem(pyside_globals->arg_dict, type_key);
- AutoDecRef strings(_address_to_stringlist(numkey));
+ if (PyTuple_Check(numkey)) {
+ PyObject *obAddress = PyTuple_GetItem(numkey, 0);
+ PyObject *obSize = PyTuple_GetItem(numkey, 1);
+ const void *addr = PyLong_AsVoidPtr(obAddress);
+ const Py_ssize_t size = PyLong_AsSsize_t(obSize);
+ const char **cstrings = bytesToStrings(reinterpret_cast<const uint8_t *>(addr), size);
+ if (cstrings == nullptr)
+ return nullptr;
+ strings.reset(_address_ptr_to_stringlist(cstrings));
+ } else {
+ strings.reset(_address_to_stringlist(numkey));
+ }
if (strings.isNull())
return nullptr;
AutoDecRef arg_tup(Py_BuildValue("(OO)", type_key, strings.object()));
#endif
-static int PySide_FinishSignatures(PyObject *module, const char *signatures[])
+static int _finishSignaturesCommon(PyObject *module)
{
-#ifdef PYPY_VERSION
- static const bool have_problem = get_lldebug_flag();
- if (have_problem)
- return 0; // crash with lldebug at `PyDict_Next`
-#endif
- /*
- * Initialization of module functions and resolving of static methods.
- */
- const char *name = PyModule_GetName(module);
- if (name == nullptr)
- return -1;
-
- // we abuse the call for types, since they both have a __name__ attribute.
- if (PySide_BuildSignatureArgs(module, signatures) < 0)
- return -1;
-
/*
* Note: This function crashed when called from PySide_BuildSignatureArgs.
* Probably this was an import timing problem.
* to the PyCFunction attributes. Therefore I simplified things
* and always use our own mapping.
*/
- PyObject *key, *func, *obdict = PyModule_GetDict(module);
+ PyObject *key{};
+ PyObject *func{};
+ PyObject *obdict = PyModule_GetDict(module);
Py_ssize_t pos = 0;
- while (PyDict_Next(obdict, &pos, &key, &func))
+ // Here we collect all global functions to finish our mapping.
+ while (PyDict_Next(obdict, &pos, &key, &func)) {
if (PyCFunction_Check(func))
if (PyDict_SetItem(pyside_globals->map_dict, func, module) < 0)
return -1;
+ }
// The finish_import function will not work the first time since phase 2
// was not yet run. But that is ok, because the first import is always for
// the shiboken module (or a test module).
+ [[maybe_unused]] const char *name = PyModule_GetName(module);
if (pyside_globals->finish_import_func == nullptr) {
assert(strncmp(name, "PySide6.", 8) != 0);
return 0;
}
+ // Call a Python function which has to finish something as well.
AutoDecRef ret(PyObject_CallFunction(
pyside_globals->finish_import_func, "(O)", module));
return ret.isNull() ? -1 : 0;
}
+static int PySide_FinishSignatures(PyObject *module, const char *signatures[])
+{
+#ifdef PYPY_VERSION
+ static const bool have_problem = get_lldebug_flag();
+ if (have_problem)
+ return 0; // crash with lldebug at `PyDict_Next`
+#endif
+
+ // Initialization of module functions and resolving of static methods.
+ const char *name = PyModule_GetName(module);
+ if (name == nullptr)
+ return -1;
+
+ // we abuse the call for types, since they both have a __name__ attribute.
+ if (PySide_BuildSignatureArgs(module, signatures) < 0)
+ return -1;
+ return _finishSignaturesCommon(module);
+}
+
+static int PySide_FinishSignaturesByte(PyObject *module, const uint8_t signatures[], size_t size)
+{
+#ifdef PYPY_VERSION
+ static const bool have_problem = get_lldebug_flag();
+ if (have_problem)
+ return 0; // crash with lldebug at `PyDict_Next`
+#endif
+ const char *name = PyModule_GetName(module);
+ if (name == nullptr)
+ return -1;
+
+ if (PySide_BuildSignatureArgsByte(module, signatures, size) < 0)
+ return -1;
+ return _finishSignaturesCommon(module);
+}
+
////////////////////////////////////////////////////////////////////////////
//
// External functions interface
return ret;
}
-void FinishSignatureInitialization(PyObject *module, const char *signatures[])
+int InitSignatureBytes(PyTypeObject *type, const uint8_t signatures[], size_t size)
+{
+ // PYSIDE-2701: Store the compressed bytes and produce input for
+ // InitSignatureStrings later.
+ init_shibokensupport_module();
+ auto *obType = reinterpret_cast<PyObject *>(type);
+ const int ret = PySide_BuildSignatureArgsByte(obType, signatures, size);
+ if (ret < 0 || _build_func_to_type(obType) < 0) {
+ PyErr_Print();
+ PyErr_SetNone(PyExc_ImportError);
+ }
+ return ret;
+}
+
+int FinishSignatureInitialization(PyObject *module, const char *signatures[])
{
/*
* This function is called at the very end of a module initialization.
PyErr_Print();
PyErr_SetNone(PyExc_ImportError);
}
+ return 0;
+}
+
+int FinishSignatureInitBytes(PyObject *module, const uint8_t signatures[], size_t size)
+{
+ init_shibokensupport_module();
+
+#ifndef PYPY_VERSION
+ static const bool patch_types = true;
+#else
+ // PYSIDE-535: On PyPy we cannot patch builtin types. This can be
+ // re-implemented later. For now, we use `get_signature`, instead.
+ static const bool patch_types = false;
+#endif
+
+ if ((patch_types && PySide_PatchTypes() < 0)
+ || PySide_FinishSignaturesByte(module, signatures, size) < 0) {
+ return -1;
+ }
+ return 0;
}
static PyObject *adjustFuncName(const char *func_name)
assert(PyType_Check(obtype)); // This was not true for __init__!
// Find the feature flags
- auto type = reinterpret_cast<PyTypeObject *>(obtype.object());
+ auto *type = reinterpret_cast<PyTypeObject *>(obtype.object());
AutoDecRef dict(PepType_GetDict(type));
int id = currentSelectId(type);
id = id < 0 ? 0 : id; // if undefined, set to zero
// PYSIDE-1305: Handle errors set by fillQtProperties.
if (PyErr_Occurred()) {
- PyObject *e, *v, *t;
+ PyObject *e{};
+ PyObject *v{};
+ PyObject *t{};
// Note: These references are all borrowed.
PyErr_Fetch(&e, &v, &t);
Py_DECREF(e);
PyErr_Print();
Py_FatalError("seterror_argument did not receive a result");
}
- PyObject *err, *msg;
+ PyObject *err{};
+ PyObject *msg{};
if (!PyArg_UnpackTuple(res, func_name, 2, 2, &err, &msg)) {
PyErr_Print();
Py_FatalError("unexpected failure in seterror_argument");
static PyObject *handle_doc(PyObject *ob, PyObject *old_descr)
{
AutoDecRef ob_type_mod(GetClassOrModOf(ob));
- const char *name;
bool isModule = PyModule_Check(ob_type_mod.object());
- if (isModule)
- name = PyModule_GetName(ob_type_mod.object());
- else
- name = reinterpret_cast<PyTypeObject *>(ob_type_mod.object())->tp_name;
+ const char *name = isModule
+ ? PyModule_GetName(ob_type_mod.object())
+ : reinterpret_cast<PyTypeObject *>(ob_type_mod.object())->tp_name;
PyObject *res{};
if (handle_doc_in_progress || name == nullptr
AutoDecRef wrap_descr(PyObject_GetAttrString(
reinterpret_cast<PyObject *>(Py_TYPE(Py_True)), "__add__"));
// abbreviations for readability
- auto md_gs = new_PyMethodDescr_getsets;
- auto md_doc = &old_md_doc_descr;
- auto cf_gs = new_PyCFunction_getsets;
- auto cf_doc = &old_cf_doc_descr;
- auto sm_gs = new_PyStaticMethod_getsets;
- auto sm_doc = &old_sm_doc_descr;
- auto wd_gs = new_PyWrapperDescr_getsets;
- auto wd_doc = &old_wd_doc_descr;
+ auto *md_gs = new_PyMethodDescr_getsets;
+ auto *md_doc = &old_md_doc_descr;
+ auto *cf_gs = new_PyCFunction_getsets;
+ auto *cf_doc = &old_cf_doc_descr;
+ auto *sm_gs = new_PyStaticMethod_getsets;
+ auto *sm_doc = &old_sm_doc_descr;
+ auto *wd_gs = new_PyWrapperDescr_getsets;
+ auto *wd_doc = &old_wd_doc_descr;
if (meth_descr.isNull() || wrap_descr.isNull()
|| PyType_Ready(Py_TYPE(meth_descr)) < 0
return p;
- } while (0);
+ } while (false);
PyErr_Print();
Py_FatalError("could not initialize part 1");
static int init_phase_2(safe_globals_struc *p, PyMethodDef *methods)
{
do {
- PyMethodDef *ml;
-
// The single function to be called, but maybe more to come.
- for (ml = methods; ml->ml_name != nullptr; ml++) {
+ for (PyMethodDef *ml = methods; ml->ml_name != nullptr; ++ml) {
PyObject *v = PyCFunction_NewEx(ml, nullptr, nullptr);
if (v == nullptr
|| PyObject_SetAttrString(p->helper_module, ml->ml_name, v) != 0)
static void handler(int sig) {
#if defined(__GLIBC__)
void *array[30];
- size_t size;
-
// get void *'s for all entries on the stack
- size = backtrace(array, 30);
+ const int size = backtrace(array, 30);
// print out all the frames to stderr
#endif
#include "signature_p.h"
+#include <cstring>
+
using namespace Shiboken;
extern "C" {
if (PyDict_SetItemString(dict, gsp->name, descr) < 0)
return -1;
}
- PyType_Modified(type);
return 0;
}
* but does not create a descriptor.
* XXX Maybe we can get rid of this, completely?
*/
- auto obtype = reinterpret_cast<PyObject *>(type);
- int len = strlen(new_name);
- auto name = new char[len + 1];
- strcpy(name, new_name);
- auto new_meth = new PyMethodDef;
+ auto *obtype = reinterpret_cast<PyObject *>(type);
+ const size_t len = std::strlen(new_name);
+ auto *name = new char[len + 1];
+ std::strcpy(name, new_name);
+ auto *new_meth = new PyMethodDef;
new_meth->ml_name = name;
new_meth->ml_meth = meth->ml_meth;
new_meth->ml_flags = meth->ml_flags;
if (list.isNull())
return nullptr;
for (int idx = 0; idx < len; ++idx) {
- auto multi_entry = PyList_GetItem(multi, idx);
- auto dup = PyDict_Copy(multi_entry);
+ auto *multi_entry = PyList_GetItem(multi, idx);
+ auto *dup = PyDict_Copy(multi_entry);
if (PyDict_SetItem(dup, PyName::name(), new_name) < 0)
return nullptr;
if (PyList_SetItem(list, idx, dup) < 0)
int insert_snake_case_variants(PyObject *dict)
{
AutoDecRef snake_dict(PyDict_New());
- PyObject *key, *value;
+ PyObject *key{};
+ PyObject *value{};
Py_ssize_t pos = 0;
while (PyDict_Next(dict, &pos, &key, &value)) {
AutoDecRef name(String::getSnakeCaseName(key, true));
return PyObject_GetAttr(ob, PyMagicName::objclass());
}
-PyObject *_address_to_stringlist(PyObject *numkey)
+PyObject *_address_ptr_to_stringlist(const char **sig_strings)
{
- /*
- * This is a tiny optimization that saves initialization time.
- * Instead of creating all Python strings during the call to
- * `PySide_BuildSignatureArgs`, we store the address of the stringlist.
- * When needed in `PySide_BuildSignatureProps`, the strings are
- * finally materialized.
- */
- Py_ssize_t address = PyNumber_AsSsize_t(numkey, PyExc_ValueError);
- if (address == -1 && PyErr_Occurred())
- return nullptr;
- char **sig_strings = reinterpret_cast<char **>(address);
PyObject *res_list = PyList_New(0);
if (res_list == nullptr)
return nullptr;
for (; *sig_strings != nullptr; ++sig_strings) {
- char *sig_str = *sig_strings;
+ const char *sig_str = *sig_strings;
AutoDecRef pystr(Py_BuildValue("s", sig_str));
if (pystr.isNull() || PyList_Append(res_list, pystr) < 0)
return nullptr;
return res_list;
}
+PyObject *_address_to_stringlist(PyObject *numkey)
+{
+ /*
+ * This is a tiny optimization that saves initialization time.
+ * Instead of creating all Python strings during the call to
+ * `PySide_BuildSignatureArgs`, we store the address of the stringlist.
+ * When needed in `PySide_BuildSignatureProps`, the strings are
+ * finally materialized.
+ */
+ void *address = PyLong_AsVoidPtr(numkey);
+ if (address == nullptr && PyErr_Occurred())
+ return nullptr;
+ return _address_ptr_to_stringlist(reinterpret_cast<const char **>(address));
+}
+
int _build_func_to_type(PyObject *obtype)
{
/*
if (descr == nullptr)
return -1;
char mangled_name[200];
- strcpy(mangled_name, meth->ml_name);
- strcat(mangled_name, ".overload");
+ std::strcpy(mangled_name, meth->ml_name);
+ std::strcat(mangled_name, ".overload");
if (PyDict_SetItemString(dict, mangled_name, descr) < 0)
return -1;
if (meth->ml_flags & METH_STATIC) {
PyObject *_get_class_of_sm(PyObject *ob_sm);
PyObject *_get_class_of_descr(PyObject *ob);
PyObject *_address_to_stringlist(PyObject *numkey);
+PyObject *_address_ptr_to_stringlist(const char **sig_strings);
int _build_func_to_type(PyObject *obtype);
int _finish_nested_classes(PyObject *dict);
}
} // namespace Shiboken
-
} // namespace Shiboken
#endif // THREADSTATESAVER_H
-
#include "sbkconverter.h"
#include "basewrapper.h"
#include "basewrapper_p.h"
+#include "sbktypefactory.h"
extern "C"
{
int SbkVoidPtrObject_init(PyObject *self, PyObject *args, PyObject *kwds)
{
- PyObject *addressObject;
+ PyObject *addressObject{};
Py_ssize_t size = -1;
int isWritable = 0;
auto *sbkSelf = reinterpret_cast<SbkVoidPtrObject *>(self);
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import sys
import os
import subprocess
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
major_version = "@shiboken_MAJOR_VERSION@"
minor_version = "@shiboken_MINOR_VERSION@"
from shiboken6 import Shiboken
-class Object(object):
+class Object:
def __init__(self) -> None: ...
-class VoidPtr(object):
+class VoidPtr:
def __init__(self, value: int) -> None: ...
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
# this file intentionally left blank
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
# flake8: noqa F:821
# flake8: noqa F:401
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
fix-complaints.py
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
-__all__ = "get_signature layout mapping lib".split()
+__all__ = ["get_signature", "make_snake_case_name", "layout", "mapping", "lib"]
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
# flake8: noqa E:721
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
importhandler.py
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
layout.py
from types import SimpleNamespace
from textwrap import dedent
-from shibokensupport.signature.mapping import ellipsis
+from shibokensupport.signature.mapping import ellipsis, missing_optional_return
+from shibokensupport.signature.parser import using_snake_case
+from shibokensupport.signature import make_snake_case_name
+
+DEFAULT_PARAM_KIND = inspect.Parameter.POSITIONAL_ONLY
class SignatureLayout(SimpleNamespace):
signature.parameters[key].__class__ = NamelessParameter
-_POSITIONAL_ONLY = inspect._POSITIONAL_ONLY # noqa E:201
-_POSITIONAL_OR_KEYWORD = inspect._POSITIONAL_OR_KEYWORD # noqa E:201
-_VAR_POSITIONAL = inspect._VAR_POSITIONAL # noqa E:201
-_KEYWORD_ONLY = inspect._KEYWORD_ONLY # noqa E:201
-_VAR_KEYWORD = inspect._VAR_KEYWORD # noqa E:201
-_empty = inspect._empty # noqa E:201
+_POSITIONAL_ONLY = inspect.Parameter.POSITIONAL_ONLY # noqa E:201
+_POSITIONAL_OR_KEYWORD = inspect.Parameter.POSITIONAL_OR_KEYWORD # noqa E:201
+_VAR_POSITIONAL = inspect.Parameter.VAR_POSITIONAL # noqa E:201
+_KEYWORD_ONLY = inspect.Parameter.KEYWORD_ONLY # noqa E:201
+_VAR_KEYWORD = inspect.Parameter.VAR_KEYWORD # noqa E:201
+_empty = inspect.Parameter.empty # noqa E:201
+
+
+default_weights = {
+ typing.Any: 1000, # noqa E:241
+ bool: 101, # noqa E:241
+ int: 102, # noqa E:241
+ float: 103, # noqa E:241
+ object: 500, # noqa E:241
+}
+
+
+def get_ordering_key(anno):
+ """
+ This is the main sorting algorithm for annotations.
+ For a normal type, we use the tuple
+
+ (- length of mro(anno), 1, name)
+
+ For Union expressions, we use the minimum
+
+ (- minlen of mro(anno), len(getargs(anno)), name)
+
+ This way, Union annotations are always sorted behind normal types.
+ Addition of a `name` field ensures a unique ordering.
+
+ A special case are numeric types, which have also an ordering between them.
+ They can be handled separately, since they are all of the shortest mro.
+ """
+ typing_type = typing.get_origin(anno)
+ is_union = typing_type is typing.Union
+ if is_union:
+ # This is some Union-like construct.
+ typing_args = typing.get_args(anno)
+ parts = len(typing_args)
+
+ if defaults := list(ann for ann in typing_args if ann in default_weights):
+ # Special: look into the default weights and use the largest.
+ leng = 0
+ for ann in defaults:
+ w = default_weights[ann]
+ if w > leng:
+ leng = w
+ anno = ann
+ else:
+ # Normal: Use the union arg with the shortest mro().
+ leng = 9999
+ for ann in typing_args:
+ lng = len(ann.mro())
+ if lng < leng:
+ leng = lng
+ anno = ann
+ else:
+ leng = len(anno.mro()) if anno not in (type, None, typing.Any) else 0
+ parts = 1
+ if anno in default_weights:
+ leng = - default_weights[anno]
+ # In 3.10 only None has no name. 3.9 is worse concerning typing constructs.
+ name = anno.__name__ if hasattr(anno, "__name__") else "None"
+ # Put typing containers after the plain type.
+ if typing_type:
+ return (-leng + 100, parts, name)
+ return (-leng, parts, name)
+
+
+def sort_by_inheritance(signatures):
+ # First decorate all signatures with a key built by the mro.
+ for idx, sig in enumerate(signatures):
+ sort_order = []
+ for param in list(sig.parameters.values()):
+ sort_order.append(get_ordering_key(param.annotation))
+ signatures[idx] = sort_order, sig
+
+ # Sort the signatures and remove the key column again.
+ signatures = sorted(signatures, key=lambda x: x[0])
+ for idx, sig in enumerate(signatures):
+ signatures[idx] = sig[1]
+ return signatures
+
+
+def best_to_remove(signatures, idx1, idx2, name=None):
+ # Both have identical annotation.
+ sig1 = signatures[idx1]
+ sig2 = signatures[idx2]
+ ra1 = sig1.return_annotation
+ ra2 = sig2.return_annotation
+ # Both have equal return annotation.
+ if ra1 == ra2:
+ for p1, p2 in zip(sig1.parameters.values(), sig2.parameters.values()):
+ # Use the first with a default.
+ if p1.default is not _empty or p2.default is not _empty:
+ return idx1 if p1.default is not _empty else idx2
+ # Use the one with a return annotation.
+ return idx1 if ra1 is not None else idx2
+
+
+def _remove_ambiguous_signatures_body(signatures, name=None):
+ # By the sorting of signatures, duplicates will always be adjacent.
+ last_ann = None
+ last_idx = -1
+ to_delete = []
+ found = False
+ for idx, sig in enumerate(signatures):
+ annos = []
+ for param in list(sig.parameters.values()):
+ annos.append(param.annotation)
+ if annos == last_ann:
+ found = True
+ to_delete.append(best_to_remove(signatures, idx, last_idx, name))
+ last_ann = annos
+ last_idx = idx
+
+ if not found:
+ return False, signatures
+ new_sigs = []
+ for idx, sig in enumerate(signatures):
+ if idx not in to_delete:
+ new_sigs.append(sig)
+ return True, new_sigs
+
+
+def is_inconsistent_overload(signatures):
+ count = 0
+ for sig in signatures:
+ count += 1 if "self" in sig.parameters else 0
+ return count != 0 and count != len(signatures)
+
+
+def remove_ambiguous_signatures(signatures, name=None):
+ # This may run more than once because of indexing.
+ found, new_sigs = _remove_ambiguous_signatures_body(signatures)
+ while found:
+ found, new_sigs = _remove_ambiguous_signatures_body(new_sigs)
+ # Python cannot handle mixed methods and classmethods. Remove the latter.
+ if is_inconsistent_overload(signatures):
+ new_sigs = list(sig for sig in new_sigs if "self" in sig.parameters)
+ return new_sigs
def create_signature(props, key):
# empty signatures string
return
if isinstance(props["multi"], list):
- # multi sig: call recursively
- return list(create_signature(elem, key)
- for elem in props["multi"])
+ # multi sig: call recursively.
+ # For debugging: Print the name!
+ # name = props["multi"][0]["fullname"]
+ res = list(create_signature(elem, key) for elem in props["multi"])
+ # PYSIDE-2846: Sort multi-signatures by inheritance in order to avoid shadowing.
+ res = sort_by_inheritance(res)
+ res = remove_ambiguous_signatures(res, name=None)
+ return res if len(res) > 1 else res[0]
+
if type(key) is tuple:
_, modifier = key
else:
del annotations["return"]
# Build a signature.
- kind = inspect._POSITIONAL_OR_KEYWORD
+ kind = DEFAULT_PARAM_KIND
params = []
+ snake_flag = using_snake_case()
+
for idx, name in enumerate(varnames):
+ if name == "*":
+ # This is a switch.
+ # Important: It must have a default to simplify the calculation.
+ kind = _KEYWORD_ONLY
+ continue
if name.startswith("**"):
kind = _VAR_KEYWORD
elif name.startswith("*"):
name = name.lstrip("*")
defpos = idx - len(varnames) + len(defaults)
default = defaults[defpos] if defpos >= 0 else _empty
+ if default is not _empty:
+ if kind != _KEYWORD_ONLY:
+ kind = _POSITIONAL_OR_KEYWORD
if default is None:
ann = typing.Optional[ann]
if default is not _empty and layout.ellipsis:
default = ellipsis
+ if kind is _KEYWORD_ONLY:
+ # All these entries are properties. They might have been used already
+ # as normal parameter before and must be omitted here. Fixing that now:
+ if varnames.count(name) > 1:
+ assert varnames.count(name) == 2
+ if snake_flag and name != (new_name := make_snake_case_name(name)):
+ # Patch this name backwards because it comes earlier as property.
+ idx = varnames.index(name)
+ params[idx] = params[idx].replace(name=new_name)
+ continue
+ if snake_flag:
+ name = make_snake_case_name(name)
param = inspect.Parameter(name, kind, annotation=ann, default=default)
params.append(param)
- if kind == _VAR_POSITIONAL:
- kind = _KEYWORD_ONLY
+
+ ret_anno = annotations.get('return', _empty)
+ if ret_anno is not _empty and props["fullname"] in missing_optional_return:
+ ret_anno = typing.Optional[ret_anno]
sig = inspect.Signature(params,
- return_annotation=annotations.get('return', _empty),
+ return_annotation=ret_anno,
__validate_parameters__=False)
# the special case of nameless parameters
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
# this file intentionally left blank
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
enum_sig.py
import inspect
import sys
import types
+import typing
from shibokensupport.signature import get_signature as get_sig
+from shibokensupport.signature.layout import DEFAULT_PARAM_KIND
+from enum import Enum
"""
return thing and type(thing) in (Signal, SignalInstance)
-class ExactEnumerator(object):
+class ExactEnumerator:
"""
ExactEnumerator enumerates all signatures in a module as they are.
is desired.
"""
+ # PYSIDE-2846: This set holds functions with `__i<f>__` != `__<f>__` signature.
+ mypy_aug_ass_errors = set()
+
+ augmented_assignments = {
+ "__iadd__": "__add__",
+ "__isub__": "__sub__",
+ "__imul__": "__mul__",
+ "__idiv__": "__div__",
+ }
+
+ # PYSIDE-2846: Inheritance errors which are in QtDesigner
+ mypy_misc_class_errors = set()
+ mypy_misc_class_errors.add("QPyDesignerPropertySheetExtension")
+
def __init__(self, formatter, result_type=dict):
global EnumMeta, Signal, SignalInstance
try:
with self.fmt.module(mod_name):
module = sys.modules[mod_name]
members = inspect.getmembers(module, inspect.isclass)
+ # PYSIDE-2846: Make sure not to get QTextFrame.iterator :(
+ members = list(x for x in members if "." not in x[0])
functions = inspect.getmembers(module, inspect.isroutine)
ret = self.result_type()
self.fmt.class_name = None
if thing_name in self.collision_candidates:
self.collision_track.add(thing_name)
+ # PYSIDE-2846: Mark inconsistency between __iadd__ and __add__ etc.
+ for aug_ass in self.augmented_assignments:
+ if aug_ass in klass.__dict__:
+ other = self.augmented_assignments[aug_ass]
+ if other in klass.__dict__:
+ aug_sig = self.get_signature(klass.__dict__[aug_ass])
+ other_sig = self.get_signature(klass.__dict__[other])
+ if aug_sig != other_sig:
+ func = klass.__dict__[aug_ass]
+ self.mypy_aug_ass_errors.add(func)
+
init_signature = getattr(klass, "__signature__", None)
+ # PYSIDE-2752: Enums without values will not have a constructor, so
+ # we set the init_signature to None, to avoid having an empty pyi
+ # entry, like:
+ # class QCborTag(enum.IntEnum):
+ # or
+ # class BeginFrameFlag(enum.Flag):
+ if issubclass(klass, Enum):
+ init_signature = None
# sort by class then enum value
enums.sort(key=lambda tup: (tup[1], tup[2].value))
self.fmt.have_body = bool(subclasses or sigs or properties or enums or # noqa W:504
init_signature or signals or attributes)
- with self.fmt.klass(class_name, class_str):
+ has_misc_error = class_name in self.mypy_misc_class_errors
+ if issubclass(klass, Enum) and not len(enums):
+ # PYSIDE-2846: We keep the empty enum and ignore the error.
+ has_misc_error = True
+ with self.fmt.klass(class_name, class_str, has_misc_error):
self.fmt.level += 1
self.fmt.class_name = class_name
if hasattr(self.fmt, "enum"):
if decorator in self.collision_track:
decorator = f"builtins.{decorator}"
signature = self.get_signature(func, decorator)
+ # PYSIDE-2846: Special cases of signatures which inherit from object.
+ _self = inspect.Parameter("self", DEFAULT_PARAM_KIND)
+ if func_name == "__dir__":
+ signature = inspect.Signature([_self], return_annotation=typing.Iterable[str])
+ elif func_name == "__repr__":
+ signature = inspect.Signature([_self], return_annotation=str)
if signature is not None:
- with self.fmt.function(func_name, signature, decorator) as key:
+ aug_ass = func in self.mypy_aug_ass_errors
+ with self.fmt.function(func_name, signature, decorator, aug_ass) as key:
ret[key] = signature
del self.func
return ret
ret = self.result_type()
signature = get_sig(func, 'existence')
sig = stringify(signature) if signature is not None else None
- if sig is not None and func_name not in ("next", "__next__", "__div__"):
+ if sig is not None:
with self.fmt.function(func_name, sig) as key:
ret[key] = sig
return ret
def __init__(self, *args, **kwds):
super().__init__(*args, **kwds)
# We need to provide default signatures for class properties.
- cls_param = inspect.Parameter("cls", inspect._POSITIONAL_OR_KEYWORD)
- set_param = inspect.Parameter("arg_1", inspect._POSITIONAL_OR_KEYWORD, annotation=object)
+ cls_param = inspect.Parameter("cls", DEFAULT_PARAM_KIND)
+ set_param = inspect.Parameter("arg_1", DEFAULT_PARAM_KIND, annotation=object)
self.getter_sig = inspect.Signature([cls_param], return_annotation=object)
self.setter_sig = inspect.Signature([cls_param, set_param])
self.deleter_sig = inspect.Signature([cls_param])
LICENSE_TEXT = """
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
# flake8: noqa E:402
"""
import argparse
+import inspect
import io
import logging
-import os
import re
import sys
import typing
from shibokensupport.signature.lib.enum_sig import HintingEnumerator
from shibokensupport.signature.lib.tool import build_brace_pattern
-# Can we use forward references?
-USE_PEP563 = sys.version_info[:2] >= (3, 7)
-
indent = " " * 4
-class Writer(object):
+class Writer:
def __init__(self, outfile, *args):
self.outfile = outfile
self.history = [True, True]
The separation in formatter and enumerator is done to keep the
unrelated tasks of enumeration and formatting apart.
"""
+
def __init__(self, outfile, options, *args):
- # XXX Find out which of these patches is still necessary!
self.options = options
Writer.__init__(self, outfile, *args)
- # patching __repr__ to disable the __repr__ of typing.TypeVar:
- """
- def __repr__(self):
- if self.__covariant__:
- prefix = '+'
- elif self.__contravariant__:
- prefix = '-'
- else:
- prefix = '~'
- return prefix + self.__name__
- """
- def _typevar__repr__(self):
- return f"typing.{self.__name__}"
- # This is no longer necessary for modern typing versions.
- # Ignore therefore if the repr is read-only and cannot be changed.
- try:
- typing.TypeVar.__repr__ = _typevar__repr__
- except TypeError:
- pass
- # Adding a pattern to substitute "Union[T, NoneType]" by "Optional[T]"
- # I tried hard to replace typing.Optional by a simple override, but
- # this became _way_ too much.
- # See also the comment in layout.py .
- brace_pat = build_brace_pattern(3, ",")
- pattern = fr"\b Union \s* \[ \s* {brace_pat} \s*, \s* NoneType \s* \]"
- replace = r"Optional[\1]"
- optional_searcher = re.compile(pattern, flags=re.VERBOSE)
-
- def optional_replacer(source):
- return optional_searcher.sub(replace, str(source))
- self.optional_replacer = optional_replacer
- # self.level is maintained by enum_sig.py
- # self.is_method() is true for non-plain functions.
+
+ # Re-add the `typing` prefix that inspect would throw away.
+ # We do that by overwriting the relevant part of the function.
+
+ backup = inspect.formatannotation
+
+ @classmethod
+ def formatannotation(cls, annotation, base_module=None):
+ if getattr(annotation, '__module__', None) == 'typing':
+ # do not remove the prefix!
+ return repr(annotation)
+ # do the normal action.
+ return cls.backup(annotation, base_module)
+
+ @classmethod
+ def fix_typing_prefix(cls, signature):
+ # modify the module, format the signature, restore the module.
+ inspect.formatannotation = cls.formatannotation
+ stringized = str(signature)
+ inspect.formatannotation = cls.backup
+ return stringized
+
+ # Adding a pattern to substitute "Union[T, NoneType]" by "Optional[T]"
+ # I tried hard to replace typing.Optional by a simple override, but
+ # this became _way_ too much.
+ # See also the comment in layout.py .
+
+ # PYSIDE-2786: Since Python 3.9, we can use the "|" notation.
+ # Transform "Union" and "Optional" this way.
+ brace_pat = build_brace_pattern(3, ",=")
+ opt_uni_searcher = re.compile(fr"""
+ \b # edge of a word
+ (typing\.Optional |
+ typing\.Union) # word to find
+ \s* # optional whitespace
+ (?= \[ ) # Lookahead enforces a square bracket
+ {brace_pat} # braces tower, one capturing brace level
+ """, flags=re.VERBOSE)
+ brace_searcher = re.compile(brace_pat, flags=re.VERBOSE)
+ split = brace_searcher.split
+
+ @classmethod
+ def optional_replacer(cls, source):
+ # PYSIDE-2517: findChild/findChildren type hints:
+ # PlaceHolderType fix to avoid the '~' from TypeVar.__repr__
+ if "~PlaceHolderType" in source:
+ source = source.replace("~PlaceHolderType", "PlaceHolderType")
+
+ while match := cls.opt_uni_searcher.search(source):
+ start = match.start()
+ end = match.end()
+ name = match.group(1)
+ body = match.group(2).strip()[1:-1]
+ # Note: this list is interspersed with "," and surrounded by "", see parser.py
+ parts = [x.strip() for x in cls.split(body) if x.strip() not in ("", ",")]
+ if name == "typing.Optional":
+ parts.append("None")
+ res = " | ".join(parts)
+ source = source[: start] + res + source[end :]
+ # Replace all "NoneType" strings by "None" which is a typing convention.
+ return source.replace("NoneType", "None")
+
+ # self.level is maintained by enum_sig.py
+ # self.is_method() is true for non-plain functions.
def section(self):
if self.level == 0:
yield
@contextmanager
- def klass(self, class_name, class_str):
+ def klass(self, class_name, class_str, has_misc_error=None):
+ err_ignore = " # type: ignore[misc]"
+ opt_comment = err_ignore if has_misc_error else ""
spaces = indent * self.level
while "." in class_name:
class_name = class_name.split(".", 1)[-1]
class_str = class_str.split(".", 1)[-1]
if self.have_body:
- self.print(f"{spaces}class {class_str}:")
+ self.print(f"{spaces}class {class_str}:{opt_comment}")
else:
- self.print(f"{spaces}class {class_str}: ...")
+ self.print(f"{spaces}class {class_str}: ...{opt_comment}")
yield
@contextmanager
- def function(self, func_name, signature, decorator=None):
+ def function(self, func_name, signature, decorator=None, aug_ass=None):
if func_name == "__init__":
self.print()
key = func_name
spaces = indent * self.level
+ err_ignore = " # type: ignore[misc]"
if isinstance(signature, list):
+ # PYSIDE-2846: Disable errors in augmented assignments.
+ opt_comment = (err_ignore if aug_ass else "")
for sig in signature:
- self.print(f'{spaces}@overload')
+ self.print(f'{spaces}@typing.overload{opt_comment}')
+ opt_comment = ""
self._function(func_name, sig, spaces)
else:
- self._function(func_name, signature, spaces, decorator)
+ opt_comment = err_ignore if aug_ass else ""
+ self._function(func_name, signature, spaces, decorator, opt_comment)
if func_name == "__init__":
self.print()
yield key
- def _function(self, func_name, signature, spaces, decorator=None):
+ def _function(self, func_name, signature, spaces, decorator=None, opt_comment=""):
if decorator:
# In case of a PyClassProperty the classmethod decorator is not used.
self.print(f'{spaces}@{decorator}')
elif self.is_method() and "self" not in signature.parameters:
kind = "class" if "cls" in signature.parameters else "static"
self.print(f'{spaces}@{kind}method')
+ # the formatting with the inspect module explicitly removes the `typing` prefix.
+ signature = self.fix_typing_prefix(signature)
+ # from now on, the signature will be stringized.
signature = self.optional_replacer(signature)
- self.print(f'{spaces}def {func_name}{signature}: ...')
+ self.print(f'{spaces}def {func_name}{signature}: ...{opt_comment}')
@contextmanager
def enum(self, class_name, enum_name, value):
spaces = indent * self.level
hexval = hex(value)
- self.print(f"{spaces}{enum_name:25}: {class_name} = ... # {hexval}")
+ self.print(f"{spaces}{enum_name:25} = ... # {hexval if value >= 0 else value}")
yield
@contextmanager
def attribute(self, attr_name, attr_value):
spaces = indent * self.level
- self.print(f"{spaces}{attr_name:25} = ... # type: {type(attr_value).__qualname__}")
+ # PYSIDE-2903: Use a fully qualified name in the type comment.
+ full_name = f"{type(attr_value).__module__}.{type(attr_value).__qualname__}"
+ self.print(f"{spaces}{attr_name:25} = ... # type: {full_name}")
yield
@contextmanager
def signal(self, class_name, sig_name, sig_str):
spaces = indent * self.level
- self.print(f"{spaces}{sig_name:25}: ClassVar[{class_name}] = ... # {sig_str}")
+ self.print(f"{spaces}{sig_name:25}: typing.ClassVar[{class_name}] = ... # {sig_str}")
yield
(None, ["builtins"]),
(None, ["os"]),
(None, ["enum"]),
+ (None, ["typing"]),
("collections.abc", ["Iterable"]),
- ("typing", sorted(typing.__all__)),
("PySide6.QtCore", ["PyClassProperty", "Signal", "SignalInstance"]),
("shiboken6", ["Shiboken"]),
]
outfile = io.StringIO()
fmt = Formatter(outfile, options)
fmt.print(LICENSE_TEXT.strip())
- if USE_PEP563:
- fmt.print("from __future__ import annotations")
- fmt.print()
fmt.print(dedent(f'''\
"""
This file contains the exact signatures for all functions in module
{import_name}, except for defaults which are replaced by "...".
+
+ # mypy: disable-error-code="override, overload-overlap"
"""
'''))
HintingEnumerator(fmt).module(import_name)
wr.print("import " + imp)
wr.print()
for mod, imports in filter_from_imports(FROM_IMPORTS, text):
- import_args = ', '.join(imports)
+ # Sorting, and getting uniques to avoid duplications
+ # on "Iterable" having a couple of entries.
+ import_args = ', '.join(sorted(set(imports)))
if mod is None:
# special case, a normal import
wr.print(f"import {import_args}")
wr.print(f"from {mod} import {import_args}")
wr.print()
wr.print()
- wr.print("NoneType: TypeAlias = type[None]")
+ # We use it only in QtCore at the moment, but this
+ # could be extended to other modules. (must import QObject then)
+ if import_name == "PySide6.QtCore":
+ wr.print("PlaceHolderType = typing.TypeVar(\"PlaceHolderType\", "
+ "bound=PySide6.QtCore.QObject)")
wr.print()
else:
wr.print(line)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
tool.py
There are other regex engines available which allow recursive patterns,
avoiding this problem completely. It might be considered to switch to
such an engine if the external module is not a problem.
+
+ Note: This pattern has exactly one capturing group.
"""
assert type(separators) is str
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
# flake8: noqa E:402
# flake8: noqa F:401
import signature_bootstrap
from shibokensupport import signature, feature
signature.get_signature = signature_bootstrap.get_signature
+signature.make_snake_case_name = signature_bootstrap.make_snake_case_name
# PYSIDE-1019: Publish the __feature__ dictionary.
feature.pyside_feature_dict = signature_bootstrap.pyside_feature_dict
builtins.__feature_import__ = signature_bootstrap.__feature_import__
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
# flake8: noqa E:203
from _imp import is_builtin
-class ellipsis(object):
+class ellipsis:
def __repr__(self):
return "..."
+if not hasattr(typing, "Self"):
+ @typing._SpecialForm
+ def Self(self, parameters):
+ raise TypeError(f"{self} is not subscriptable")
+ typing.Self = Self
+
ellipsis = ellipsis()
Point = typing.Tuple[int, int]
Variant = typing.Any
-QImageCleanupFunction = typing.Callable
+QImageCleanupFunction = typing.Callable[..., typing.Any]
# unfortunately, typing.Optional[t] expands to typing.Union[t, NoneType]
# Until we can force it to create Optional[t] again, we use this.
NoneType = type(None)
+# PYSIDE-2517: findChild/findChildren type hints:
+# Placeholder so it does not trigger an UNDEFINED error while building.
+# Later it will be bound to a QObject, within the QtCore types extensions
+PlaceHolderType = TypeVar("PlaceHolderType")
+
_S = TypeVar("_S")
MultiMap = typing.DefaultDict[str, typing.List[str]]
return eval(text, namespace)
-USE_PEP563 = False
-# Note: we cannot know if this feature has been imported.
-# Otherwise it would be "sys.version_info[:2] >= (3, 7)".
-# We *can* eventually inspect sys.modules and look if
-# the calling module has this future statement set,
-# but should we do that?
+USE_PEP563 = sys.version_info[:2] >= (3, 10)
# Some types are abstract. They just show their name.
pass
+class KeywordOnly(_NotCalled):
+ pass
+
+
# Parameterized primitive variables
-class _Parameterized(object):
+class _Parameterized:
def __init__(self, type):
self.type = type
self.__name__ = self.__class__.__name__
StringList = ArrayLikeVariable(str)
-class Reloader(object):
+class Reloader:
"""
Reloder class
# Modules are in place, we can update the type_map.
g.update(g.pop(proc_name)())
+ # Also record an efficient list of modules for PySide.
+ if mod_name.startswith("PySide6."):
+ pyside_modules.add(mod_name)
+
def check_module(mod):
# During a build, there exist the modules already as directories,
update_mapping = Reloader().update
type_map = {}
+type_map_tuple = {}
namespace = globals() # our module's __dict__
+pyside_modules: set[str] = set()
type_map.update({
"...": ellipsis,
"int": int,
"List": ArrayLikeVariable,
"Optional": typing.Optional,
+ "Iterable": typing.Iterable,
"long": int,
"long long": int,
"nullptr": None,
- "PyCallable": typing.Callable,
+ "PyCallable": typing.Callable[..., typing.Any],
"PyObject": object,
"PyObject*": object,
- "PyArrayObject": ArrayLikeVariable, # numpy
+ "PyArrayObject": ArrayLikeVariable(typing.Any), # numpy
"PyPathLike": typing.Union[str, bytes, os.PathLike[str]],
"PySequence": typing.Iterable, # important for numpy
"PyTypeObject": type,
"ushort": int,
"void": int, # be more specific?
"WId": WId,
- "zero(bytes)": b"",
- "zero(Char)": 0,
- "zero(float)": 0,
- "zero(int)": 0,
- "zero(object)": None,
- "zero(str)": "",
- "zero(typing.Any)": None,
- "zero(Any)": None,
# This can be refined by importing numpy.typing optionally, but better than nothing.
"numpy.ndarray": typing.List[typing.Any],
"std.array[int, 4]": typing.List[int],
- "std.array[float, 4]": typing.List[float]
+ "std.array[float, 4]": typing.List[float],
})
type_map.update({
"quint8*" : bytearray, # only for QCborStreamReader and QCborValue
"uchar*" : typing.Union[bytes, bytearray, memoryview],
"unsigned char*": typing.Union[bytes, bytearray, memoryview],
+ "char*const" : typing.Union[bytes, bytearray, memoryview],
})
type_map.update({
# PYSIDE-1538: We need to treat "std::optional" accordingly.
type_map.update({
"std.optional": typing.Optional,
- })
+})
+# PYSIDE-2846: A special keyord only switching token.
+type_map.update({
+ "KeywordOnly": KeywordOnly("None"),
+})
# The Shiboken Part
def init_Shiboken():
# The PySide Part
def init_PySide6_QtCore():
- from PySide6.QtCore import Qt, QUrl, QDir, QKeyCombination
- from PySide6.QtCore import QRect, QRectF, QSize, QPoint, QLocale, QByteArray
+ from PySide6.QtCore import Qt, QUrl, QDir, QByteArray
+ from PySide6.QtCore import QRect, QRectF, QSize, QPoint
from PySide6.QtCore import QMarginsF # 5.9
- from PySide6.QtCore import SignalInstance
- try:
- # seems to be not generated by 5.9 ATM.
- from PySide6.QtCore import Connection
- except ImportError:
- pass
+
type_map.update({
"' '": " ",
"'%'": "%",
"size_t": int,
"NULL": None, # 5.6, MSVC
"nullptr": None, # 5.9
+ # PYSIDE-2517: findChild/findChildren type hints:
+ "PlaceHolderType": typing.TypeVar("PlaceHolderType", bound=PySide6.QtCore.QObject),
"PyBuffer": typing.Union[bytes, bytearray, memoryview],
"PyByteArray": bytearray,
"PyBytes": typing.Union[bytes, bytearray, memoryview],
"PyTuple": typing.Tuple,
+ "QDeadlineTimer.Forever": PySide6.QtCore.QDeadlineTimer.ForeverConstant.Forever,
"QDeadlineTimer(QDeadlineTimer.Forever)": Instance("PySide6.QtCore.QDeadlineTimer"),
"PySide6.QtCore.QUrl.ComponentFormattingOptions":
PySide6.QtCore.QUrl.ComponentFormattingOption, # mismatch option/enum, why???
"PyUnicode": typing.Text,
- "QByteArrayView": QByteArray,
+ "QByteArrayView": PySide6.QtCore.QByteArray,
"Q_NULLPTR": None,
"QCalendar.Unspecified": PySide6.QtCore.QCalendar.Unspecified,
"QCborTag(-1)": ulong_max,
- "QDir.Filters(AllEntries | NoDotAndDotDot)": Instance(
- "QDir.Filters(QDir.AllEntries | QDir.NoDotAndDotDot)"),
- "QDir.SortFlags(Name | IgnoreCase)": Instance(
- "QDir.SortFlags(QDir.Name | QDir.IgnoreCase)"),
"QEvent.Type.None": None,
"QGenericArgument((0))": ellipsis, # 5.6, RHEL 6.6. Is that ok?
"QGenericArgument()": ellipsis,
"QJsonObject": typing.Dict[str, PySide6.QtCore.QJsonValue],
"QModelIndex()": Invalid("PySide6.QtCore.QModelIndex"), # repr is btw. very wrong, fix it?!
"QModelIndexList": typing.List[PySide6.QtCore.QModelIndex],
- "PySideSignalInstance": SignalInstance,
+ "PySideSignalInstance": PySide6.QtCore.SignalInstance,
"QString()": "",
"Flag.Default": Instance("PySide6.QtCore.QStringConverterBase.Flags"),
"QStringList()": [],
"QStringRef": str,
"QStringRef": str,
"Qt.HANDLE": int, # be more explicit with some constants?
- "QUrl.FormattingOptions(PrettyDecoded)": Instance(
- "QUrl.FormattingOptions(QUrl.PrettyDecoded)"),
+ "QUrl.FormattingOptions(QUrl.ComponentFormattingOption.PrettyDecoded)":
+ PySide6.QtCore.QUrl.ComponentFormattingOption.PrettyDecoded,
"QVariant()": Invalid(Variant),
"QVariant.Type": type, # not so sure here...
"QVariantMap": typing.Dict[str, Variant],
"std.chrono.seconds{5}" : ellipsis,
})
- try:
+ from shibokensupport.signature.parser import using_snake_case
+ if using_snake_case():
type_map.update({
- "PySide6.QtCore.QMetaObject.Connection": PySide6.QtCore.Connection, # wrong!
+ "QKeyCombination.fromCombined(0)": PySide6.QtCore.QKeyCombination.from_combined(0),
})
- except AttributeError:
- # this does not exist on 5.9 ATM.
- pass
-
# special case - char* can either be 'bytes' or 'str'. The default is 'bytes'.
# Here we manually set it to map to 'str'.
- type_map.update({("PySide6.QtCore.QObject.setProperty", "char*"): str})
- type_map.update({("PySide6.QtCore.QObject.property", "char*"): str})
+ type_map_tuple.update({("PySide6.QtCore.QObject.setProperty", "char*"): str})
+ type_map_tuple.update({("PySide6.QtCore.QObject.property", "char*"): str})
return locals()
# special case - char* can either be 'bytes' or 'str'. The default is 'bytes'.
# Here we manually set it to map to 'str'.
- type_map.update({("PySide6.QtGui.QPixmap.save", "char*"): str})
+ type_map_tuple.update({("PySide6.QtGui.QPixmap.save", "char*"): str})
return locals()
def init_PySide6_QtTest():
- from PySide6.QtCore import SignalInstance
type_map.update({
- "PySideSignalInstance": SignalInstance,
"PySide6.QtTest.QTest.PySideQTouchEventSequence": PySide6.QtTest.QTest.QTouchEventSequence,
"PySide6.QtTest.QTouchEventSequence": PySide6.QtTest.QTest.QTouchEventSequence,
})
})
return locals()
+
+# Functions which should return Optional(result) but don't.
+missing_optional_return = {
+ "PySide6.QtWidgets.QApplication.activeModalWidget",
+ "PySide6.QtWidgets.QApplication.activePopupWidget",
+ "PySide6.QtWidgets.QApplication.activeWindow",
+ "PySide6.QtWidgets.QApplication.focusWidget",
+ "PySide6.QtWidgets.QApplication.setStyle",
+ "PySide6.QtWidgets.QApplication.topLevelAt",
+ "PySide6.QtWidgets.QApplication.widgetAt",
+ "PySide6.QtWidgets.QComboBox.completer",
+ "PySide6.QtWidgets.QComboBox.lineEdit",
+ "PySide6.QtWidgets.QComboBox.validator",
+ "PySide6.QtWidgets.QGridLayout.itemAt",
+ "PySide6.QtWidgets.QGridLayout.itemAtPosition",
+ "PySide6.QtWidgets.QLayout.itemAt",
+ "PySide6.QtWidgets.QTableWidget.horizontalHeaderItem",
+ "PySide6.QtWidgets.QTableWidget.item",
+ "PySide6.QtWidgets.QTableWidget.itemAt",
+ "PySide6.QtWidgets.QTableWidget.mimeData",
+ "PySide6.QtWidgets.QWidget.childAt",
+ "PySide6.QtWidgets.QWidget.find",
+ "PySide6.QtWidgets.QWidget.focusProxy",
+ "PySide6.QtWidgets.QWidget.graphicsEffect",
+ "PySide6.QtWidgets.QWidget.graphicsProxyWidget",
+ "PySide6.QtWidgets.QWidget.keyboardGrabber",
+ "PySide6.QtWidgets.QWidget.layout",
+ "PySide6.QtWidgets.QWidget.mouseGrabber",
+ "PySide6.QtWidgets.QWidget.nativeParentWidget",
+ "PySide6.QtWidgets.QWidget.nextInFocusChain",
+ "PySide6.QtWidgets.QWidget.parentWidget",
+ "PySide6.QtWidgets.QWidget.previousInFocusChain",
+}
+
# end of file
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import ast
import enum
import warnings
from types import SimpleNamespace
-from shibokensupport.signature.mapping import (type_map, update_mapping,
- namespace, _NotCalled, ResultVariable, ArrayLikeVariable) # noqa E:128
+from shibokensupport.signature.mapping import (type_map, type_map_tuple, update_mapping,
+ namespace, _NotCalled, ResultVariable, ArrayLikeVariable, pyside_modules) # noqa E:128
from shibokensupport.signature.lib.tool import build_brace_pattern
+from shibokensupport.signature import make_snake_case_name
_DEBUG = False
LIST_KEYWORDS = False
return vars(ret)
-def _using_snake_case():
+def using_snake_case():
# Note that this function should stay here where we use snake_case.
+ # This function is only meant for creating correct PYI files.
if "PySide6.QtCore" not in sys.modules:
return False
from PySide6.QtCore import QDir
return thing
start, stop = match.start(), match.end() - 1
pre, func, args = thing[:start], thing[start:stop], thing[stop:]
- if func[0].isupper() or func.startswith("gl") and func[2:3].isupper():
- return thing
- # Now convert this string to snake case.
- snake_func = ""
- for idx, char in enumerate(func):
- if char.isupper():
- if idx and func[idx - 1].isupper():
- # two upper chars are forbidden
- return thing
- snake_func += f"_{char.lower()}"
- else:
- snake_func += char
+ snake_func = make_snake_case_name(func)
return f"{pre}{snake_func}{args}"
def make_good_value(thing, valtype):
# PYSIDE-1019: Handle instance calls (which are really seldom)
- if "(" in thing and _using_snake_case():
+ if "(" in thing and using_snake_case():
thing = _handle_instance_fixup(thing)
try:
if thing.endswith("()"):
if thing in ("0", "None") and valtype:
if valtype.startswith("PySide6.") or valtype.startswith("typing."):
return None
- map = type_map[valtype]
+ mapped = type_map.get(valtype)
# typing.Any: '_SpecialForm' object has no attribute '__name__'
- name = get_name(map) if hasattr(map, "__name__") else str(map)
+ name = get_name(mapped) if hasattr(mapped, "__name__") else str(mapped)
thing = f"zero({name})"
+ type_map[f"zero({name})"] = None
if thing in type_map:
return type_map[thing]
res = make_good_value(thing, valtype)
if res is not None:
type_map[thing] = res
return res
+ # Still not found. Look into the imported modules.
+ if res := get_from_another_module(thing):
+ return res
warnings.warn(f"""pyside_type_init:_resolve_value
UNRECOGNIZED: {thing!r}
# i.e. it must be an idempotent mapping.
if isinstance(thing, str):
return thing
+ # PYSIDE-2517: findChild/findChildren type hints:
+ # TypeVar doesn't have a __qualname__ attribute,
+ # so we fall back to use __name__ before the next condition.
+ if isinstance(thing, typing.TypeVar):
+ return get_name(thing)
if hasattr(thing, "__name__") and thing.__module__ != "typing":
m = thing.__module__
dot = "." in str(thing) or m not in (thing.__qualname__, "builtins")
return eval(result, globals(), namespace)
+def get_from_another_module(thing):
+ top = thing.split(".", 1)[0] if "." in thing else thing
+ for mod_name in pyside_modules:
+ mod = sys.modules[mod_name]
+ if hasattr(mod, top):
+ try:
+ res = eval(f"{mod_name}.{thing}", globals(), namespace)
+ type_map[thing] = res
+ return res
+ except AttributeError:
+ # Maybe it was anothr module...
+ pass
+ return None
+
+
def _resolve_type(thing, line, level, var_handler, func_name=None):
# manual set of 'str' instead of 'bytes'
if func_name:
new_thing = (func_name, thing)
- if new_thing in type_map:
- return type_map[new_thing]
+ if new_thing in type_map_tuple:
+ return type_map_tuple[new_thing]
# Capture total replacements, first. Happens in
# "PySide6.QtCore.QCborStreamReader.StringResult[PySide6.QtCore.QByteArray]"
# Now the nested structures are handled.
if "[" in thing:
+ # Special case: Callable[[],
+ if thing == "[]":
+ return thing
# handle primitive arrays
if re.search(r"\[\d*\]$", thing):
thing = _resolve_arraytype(thing, line)
return _handle_generic(obj, typing.List)
+class Hashabledict(dict):
+ def __hash__(self):
+ return hash(frozenset(self))
+
+
def calculate_props(line):
parsed = SimpleNamespace(**_parse_line(line.strip()))
arglist = parsed.arglist
annotations[name] = _resolve_type(ann, line, 0, handle_argvar, parsed.funcname)
if len(tup) == 3:
default = _resolve_value(tup[2], ann, line)
+ # PYSIDE-2846: When creating signatures, the defaults should be hashable.
+ # For that to work, we use `Hashabledict`.
+ if type(default) is dict:
+ default = Hashabledict(default)
_defaults.append(default)
defaults = tuple(_defaults)
returntype = parsed.returntype
funcname = parsed.funcname
shortname = funcname[funcname.rindex(".") + 1:]
props.name = shortname
+ props.fullname = funcname
props.multi = parsed.multi
fix_variables(props, line)
return vars(props)
props.defaults = tuple(defaults)
-def fixup_multilines(lines):
- """
- Multilines can collapse when certain distinctions between C++ types
- vanish after mapping to Python.
- This function fixes this by re-computing multiline-ness.
- """
- res = []
- multi_lines = []
- for line in lines:
- multi = re.match(r"([0-9]+):", line)
- if multi:
- idx, rest = int(multi.group(1)), line[multi.end():]
- multi_lines.append(rest)
- if idx > 0:
- continue
- # remove duplicates
- multi_lines = sorted(set(multi_lines))
- # renumber or return a single line
- nmulti = len(multi_lines)
- if nmulti > 1:
- for idx, line in enumerate(multi_lines):
- res.append(f"{nmulti-idx-1}:{line}")
- else:
- res.append(multi_lines[0])
- multi_lines = []
- else:
- res.append(line)
- return res
-
-
def pyside_type_init(type_key, sig_strings):
dprint()
dprint(f"Initialization of type key '{type_key}'")
update_mapping()
- lines = fixup_multilines(sig_strings)
ret = {}
multi_props = []
- for line in lines:
+ for line in sig_strings:
props = calculate_props(line)
shortname = props["name"]
multi = props["multi"]
std::vector<void*> ptrs = Shiboken::Object::cppPointers(reinterpret_cast<SbkObject *>(%1));
%PYARG_0 = PyTuple_New(ptrs.size());
for (std::size_t i = 0; i < ptrs.size(); ++i)
- PyTuple_SET_ITEM(%PYARG_0, i, PyLong_FromVoidPtr(ptrs[i]));
+ PyTuple_SetItem(%PYARG_0, i, PyLong_FromVoidPtr(ptrs[i]));
} else {
PyErr_SetString(PyExc_TypeError, "You need a shiboken-based type.");
}
}
// @snippet dump
+// @snippet replacemoduledict
+const bool ok = Shiboken::Module::replaceModuleDict(%1, %2, %3);
+%PYARG_0 = %CONVERTTOPYTHON[bool](ok);
+// @snippet replacemoduledict
+
// @snippet getallvalidwrappers
const auto setAll = Shiboken::BindingManager::instance().getAllPyObjects();
PyObject* listAll = PyList_New(0);
Shiboken::BindingManager::instance().dumpWrapperMap();
// @snippet dumpwrappermap
+// @snippet dumpconverters
+Shiboken::Conversions::dumpConverters();
+// @snippet dumpconverters
+
// @snippet init
// Add __version__ and __version_info__ attributes to the module
PyObject* version = PyTuple_New(5);
-PyTuple_SET_ITEM(version, 0, PyLong_FromLong(SHIBOKEN_MAJOR_VERSION));
-PyTuple_SET_ITEM(version, 1, PyLong_FromLong(SHIBOKEN_MINOR_VERSION));
-PyTuple_SET_ITEM(version, 2, PyLong_FromLong(SHIBOKEN_MICRO_VERSION));
-PyTuple_SET_ITEM(version, 3, Shiboken::String::fromCString(SHIBOKEN_RELEASE_LEVEL));
-PyTuple_SET_ITEM(version, 4, PyLong_FromLong(SHIBOKEN_SERIAL));
+PyTuple_SetItem(version, 0, PyLong_FromLong(SHIBOKEN_MAJOR_VERSION));
+PyTuple_SetItem(version, 1, PyLong_FromLong(SHIBOKEN_MINOR_VERSION));
+PyTuple_SetItem(version, 2, PyLong_FromLong(SHIBOKEN_MICRO_VERSION));
+PyTuple_SetItem(version, 3, Shiboken::String::fromCString(SHIBOKEN_RELEASE_LEVEL));
+PyTuple_SetItem(version, 4, PyLong_FromLong(SHIBOKEN_SERIAL));
PyModule_AddObject(module, "__version_info__", version);
PyModule_AddStringConstant(module, "__version__", SHIBOKEN_VERSION);
VoidPtr::addVoidPtrToModule(module);
<inject-code file="shibokenmodule.cpp" snippet="disassembleframe"/>
</add-function>
+ <add-function signature="replaceModuleDict(PyObject*, PyObject*, PyObject*)" return-type="bool">
+ <inject-code file="shibokenmodule.cpp" snippet="replacemoduledict"/>
+ </add-function>
+
<add-function signature="dump(PyObject*)" return-type="const char *">
<inject-code file="shibokenmodule.cpp" snippet="dump"/>
</add-function>
<inject-code file="shibokenmodule.cpp" snippet="dumpwrappermap"/>
</add-function>
+ <add-function signature="dumpConverters()">
+ <inject-code file="shibokenmodule.cpp" snippet="dumpconverters"/>
+ </add-function>
+
<extra-includes>
<include file-name="sbkversion.h" location="local"/>
<include file-name="voidptr.h" location="local"/>
+ <include file-name="sbkconverter_p.h" location="local"/>
</extra-includes>
<inject-code position="end" file="shibokenmodule.cpp" snippet="init"/>
</typesystem>
target_include_directories(libminimal PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(libminimal PRIVATE LIBMINIMAL_BUILD)
set_property(TARGET libminimal PROPERTY PREFIX "")
-
};
#endif // LISTUSER_H
-
{
return !bool(val%2);
}
-
target_compile_definitions(libother PRIVATE LIBOTHER_BUILD)
target_link_libraries(libother PUBLIC libsample libsmart)
set_property(TARGET libother PROPERTY PREFIX "")
-
-
class ObjectType;
-class LIBOTHER_API OtherMultipleDerived : public MDerived1
+class LIBOTHER_API OtherMultipleDerived : public OtherBase, public MDerived1
{
public:
// this will use CppCopier from other module (bug#142)
target_include_directories(libsample PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(libsample PRIVATE LIBSAMPLE_BUILD)
set_property(TARGET libsample PROPERTY PREFIX "")
-
LIBSAMPLE_API Collector &operator<<(Collector &, const IntWrapper &);
#endif // COLLECTOR_H
-
void pureVirtualPrivate() override;
};
#endif // DERIVED_H
-
LIBSAMPLE_API Intersection operator&(const Intersection &a, const Intersection &b);
#endif // FILTER_H
-
-
};
#endif // INJECTCODE_H
-
};
#endif // LISTUSER_H
-
{
return m_stored_value;
}
-
-
};
#endif // MODIFIEDCONSTRUCTOR_H
-
};
#endif // NOIMPLICITCONVERSION_H
-
target_include_directories(libsmart PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(libsmart PRIVATE LIBSMART_BUILD)
set_property(TARGET libsmart PROPERTY PREFIX "")
-
std::cerr << '\n';
}
+std::shared_ptr<double> StdSharedPtrTestBench::createDouble(double v)
+{
+ return std::make_shared<double>(v);
+}
+
+std::shared_ptr<double> StdSharedPtrTestBench::createNullDouble()
+{
+ return {};
+}
+
+void StdSharedPtrTestBench::printDouble(const std::shared_ptr<double> &p)
+{
+ std::cerr << __FUNCTION__ << ' ';
+ if (p.get())
+ std::cerr << *p;
+ else
+ std::cerr << "nullptr";
+ std::cerr << '\n';
+}
+
std::shared_ptr<std::string> StdSharedPtrTestBench::createString(const char *text)
{
return std::make_shared<std::string>(text);
static std::shared_ptr<int> createNullInt();
static void printInt(const std::shared_ptr<int> &);
+ static std::shared_ptr<double> createDouble(double v = 42);
+ static std::shared_ptr<double> createNullDouble();
+ static void printDouble(const std::shared_ptr<double> &);
+
static std::shared_ptr<std::string> createString(const char *text);
static std::shared_ptr<std::string> createNullString();
static void printString(const std::shared_ptr<std::string> &);
"${CMAKE_CURRENT_BINARY_DIR}/minimal-binding.txt" @ONLY)
shiboken_get_tool_shell_wrapper(shiboken tool_wrapper)
+if(SHIBOKEN_UNOPTIMIZE)
+ SET(UNOPTIMIZE "--unoptimize=${SHIBOKEN_UNOPTIMIZE}")
+ENDIF()
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log"
${tool_wrapper}
$<TARGET_FILE:Shiboken6::shiboken6>
--project-file=${CMAKE_CURRENT_BINARY_DIR}/minimal-binding.txt
+ ${UNOPTIMIZE}
${GENERATOR_EXTRA_FLAGS}
DEPENDS ${minimal_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h Shiboken6::shiboken6
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import os
import re
#!/usr/bin/env python
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
from functools import reduce
import os
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
def testPassAndReceiveEnumValue(self):
val = Val(0)
- self.assertEqual(val.oneOrTheOtherEnumValue(Val.One), Val.Other)
- self.assertEqual(val.oneOrTheOtherEnumValue(Val.Other), Val.One)
+ self.assertEqual(val.oneOrTheOtherEnumValue(Val.ValEnum.One), Val.ValEnum.Other)
+ self.assertEqual(val.oneOrTheOtherEnumValue(Val.ValEnum.Other), Val.ValEnum.One)
def testPassValueTypeFromExtendedClass(self):
val = ExtVal(0)
"${CMAKE_CURRENT_BINARY_DIR}/other-binding.txt" @ONLY)
shiboken_get_tool_shell_wrapper(shiboken tool_wrapper)
+if(SHIBOKEN_UNOPTIMIZE)
+ SET(UNOPTIMIZE "--unoptimize=${SHIBOKEN_UNOPTIMIZE}")
+ENDIF()
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log"
${tool_wrapper}
$<TARGET_FILE:Shiboken6::shiboken6>
--project-file=${CMAKE_CURRENT_BINARY_DIR}/other-binding.txt
+ ${UNOPTIMIZE}
${GENERATOR_EXTRA_FLAGS}
DEPENDS ${other_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h Shiboken6::shiboken6
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
add_dependencies(other sample smart)
create_generator_target(other)
-
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for Collector shift operators defined in other modules.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests calling NoImplicitConversion using a ExtendsNoImplicitConversion parameter,
being that the latter defines a new conversion operator for the former, and this one
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for libsample's Point multiply operator defined in libother module.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
from importlib import reload
import os
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests calling Str constructor using a Number parameter, being that number defines
a cast operator to Str.'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for OtherDerived class'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for a class that holds a void pointer.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for functions signature'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for the SmartPtrTester class'''
#!/usr/bin/env python
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
"""PYSIDE-2404: Test whether star imports work as they require special handling
by the lazy initialization."""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for type discovery'''
from shiboken_paths import init_paths
init_paths()
-from sample import Abstract, Base1, Derived
+from sample import (Abstract, Base1, Derived,
+ MDerived1, SonOfMDerived1, MDerived3)
from other import OtherMultipleDerived
def testMultipleInheritance(self):
obj = OtherMultipleDerived.createObject("Base1")
self.assertEqual(type(obj), Base1)
- # PYSIDE-868: In case of multiple inheritance, a factory
- # function will return the base class wrapper.
+ # PYSIDE-868: In case of single line direct inheritance,
+ # a factory function will return the class wrapper
+ # of the derived class.
obj = OtherMultipleDerived.createObject("MDerived1")
- self.assertEqual(type(obj), Base1)
+ self.assertEqual(type(obj), MDerived1)
obj = OtherMultipleDerived.createObject("SonOfMDerived1")
- self.assertEqual(type(obj), Base1)
+ self.assertEqual(type(obj), SonOfMDerived1)
obj = OtherMultipleDerived.createObject("MDerived3")
- self.assertEqual(type(obj), Base1)
+ self.assertEqual(type(obj), MDerived3)
+ # PYSIDE-868: OtherMultipleDerived inherits
+ # OtherBase, Base1. In this case, a factory
+ # function will return the base class wrapper.
obj = OtherMultipleDerived.createObject("OtherMultipleDerived")
self.assertEqual(type(obj), Base1)
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests user defined primitive type from a required module.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
const QString &) const override;
const QLoggingCategory &loggingCategory() const override;
QtXmlToSphinxLink resolveLink(const QtXmlToSphinxLink &link) const override;
+ Image resolveImage(const QString &href, const QString &) const;
};
// QtXmlToSphinxDocGeneratorInterface
return lcQtXmlToSphinx();
}
-QtXmlToSphinxLink QtXmlToSphinxDocGenerator::resolveLink(const QtXmlToSphinxLink &link) const
+QtXmlToSphinxLink
+ QtXmlToSphinxDocGenerator::resolveLink(const QtXmlToSphinxLink &link) const
{
return link;
}
+QtXmlToSphinxDocGeneratorInterface::Image
+ QtXmlToSphinxDocGenerator::resolveImage(const QString &href, const QString &) const
+{
+ return {href, href};
+}
+
static bool run(const QString &fileName)
{
QtXmlToSphinxDocGenerator generator;
QString QtXmlToSphinxTest::transformXml(const QString &xml) const
{
- return QtXmlToSphinx(this, m_parameters, xml).result();
+ return QtXmlToSphinx(this, m_parameters, xml, QString{} /* context */).result();
}
void QtXmlToSphinxTest::testTable_data()
"${CMAKE_CURRENT_BINARY_DIR}/sample-binding.txt" @ONLY)
shiboken_get_tool_shell_wrapper(shiboken tool_wrapper)
+if(SHIBOKEN_UNOPTIMIZE)
+ SET(UNOPTIMIZE "--unoptimize=${SHIBOKEN_UNOPTIMIZE}")
+ENDIF()
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log"
${tool_wrapper}
$<TARGET_FILE:Shiboken6::shiboken6>
--project-file=${CMAKE_CURRENT_BINARY_DIR}/sample-binding.txt
+ ${UNOPTIMIZE}
${GENERATOR_EXTRA_FLAGS}
DEPENDS ${sample_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h Shiboken6::shiboken6
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for Abstract class'''
def testEnumParameterOnVirtualMethodCall(self):
'''testEnumParameterOnVirtualMethodCall'''
c = Concrete()
- c.callVirtualGettingEnum(Abstract.Short)
+ c.callVirtualGettingEnum(Abstract.PrintFormat.Short)
self.assertTrue(c.virtual_getting_enum)
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for added functions.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for added functions with nested and multi-argument container types.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for method arguments modifications performed as described on typesystem.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for NumPy Array types.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for Array types (PySequence).'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Unit test for bug#554'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
from sample import ObjectType
-class NewStyle(object):
+class NewStyle:
def name(self):
return "NewStyle"
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''The BlackBox class has cases of ownership transference between C++ and Python.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Simple test case for accessing the exposed C++ class fields.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for Collector class' shift operators.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for Complex class'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for implicit conversion generated by conversion operator.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for deep copy of objects'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for proper generation of constructor altered by conversion-rule tag.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for python conversions types '''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for the method overload decisor.'''
objecttype = ObjectType()
objectmodel = ObjectModel()
self.assertEqual(ObjectModel.receivesObjectTypeFamily(objecttype),
- ObjectModel.ObjectTypeCalled)
+ ObjectModel.MethodCalled.ObjectTypeCalled)
self.assertNotEqual(ObjectModel.receivesObjectTypeFamily(objecttype),
- ObjectModel.ObjectModelCalled)
+ ObjectModel.MethodCalled.ObjectModelCalled)
self.assertEqual(ObjectModel.receivesObjectTypeFamily(objectmodel),
- ObjectModel.ObjectModelCalled)
+ ObjectModel.MethodCalled.ObjectModelCalled)
self.assertNotEqual(ObjectModel.receivesObjectTypeFamily(objectmodel),
- ObjectModel.ObjectTypeCalled)
+ ObjectModel.MethodCalled.ObjectTypeCalled)
if __name__ == '__main__':
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for Derived class'''
result = derived.otherOverloaded(1, 2, True, 3.3)
self.assertEqual(type(result), Derived.OtherOverloadedFuncEnum)
- self.assertEqual(result, sample.Derived.OtherOverloadedFunc_iibd)
+ self.assertEqual(result, sample.Derived.OtherOverloadedFuncEnum.OtherOverloadedFunc_iibd)
result = derived.otherOverloaded(1, 2.2)
self.assertEqual(type(result), Derived.OtherOverloadedFuncEnum)
- self.assertEqual(result, Derived.OtherOverloadedFunc_id)
+ self.assertEqual(result, Derived.OtherOverloadedFuncEnum.OtherOverloadedFunc_id)
def testOverloadedMethodCallWithDifferentNumericTypes(self):
'''Test if the correct overloaded method accepts a different numeric type as argument.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for virtual methods.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for <add-function> with const char* as argument'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for Python representation of C++ enums.'''
'''Test case for Python representation of C++ enums.'''
def testHashability(self):
- self.assertEqual(hash(SampleNamespace.TwoIn), hash(SampleNamespace.TwoOut))
- self.assertNotEqual(hash(SampleNamespace.TwoIn), hash(SampleNamespace.OneIn))
+ self.assertEqual(hash(SampleNamespace.InValue.TwoIn), hash(SampleNamespace.OutValue.TwoOut))
+ self.assertNotEqual(hash(SampleNamespace.InValue.TwoIn),
+ hash(SampleNamespace.InValue.OneIn))
def testEnumValuesInsideEnum(self):
'''Enum values should be accessible inside the enum as well as outside.'''
def testBuildingEnumWithDefaultValue(self):
'''Enum constructor with default value'''
enum = SampleNamespace.Option()
- self.assertEqual(enum, SampleNamespace.None_)
+ self.assertEqual(enum, SampleNamespace.Option.None_)
def testEnumConversionToAndFromPython(self):
'''Conversion of enum objects from Python to C++ back again.'''
- enumout = SampleNamespace.enumInEnumOut(SampleNamespace.TwoIn)
- self.assertTrue(enumout, SampleNamespace.TwoOut)
- self.assertEqual(repr(enumout), repr(SampleNamespace.TwoOut))
+ enumout = SampleNamespace.enumInEnumOut(SampleNamespace.InValue.TwoIn)
+ self.assertTrue(enumout, SampleNamespace.OutValue.TwoOut)
+ self.assertEqual(repr(enumout), repr(SampleNamespace.OutValue.TwoOut))
def testEnumConstructorWithTooManyParameters(self):
'''Calling the constructor of non-extensible enum with the wrong number of parameters.'''
def testEnumItemAsDefaultValueToIntArgument(self):
'''Calls function with an enum item as default value to an int argument.'''
self.assertEqual(SampleNamespace.enumItemAsDefaultValueToIntArgument(),
- SampleNamespace.ZeroIn)
- self.assertEqual(SampleNamespace.enumItemAsDefaultValueToIntArgument(SampleNamespace.ZeroOut), # noqa E:501
- SampleNamespace.ZeroOut)
+ SampleNamespace.InValue.ZeroIn)
+ self.assertEqual(SampleNamespace.enumItemAsDefaultValueToIntArgument(SampleNamespace.OutValue.ZeroOut), # noqa E:501
+ SampleNamespace.OutValue.ZeroOut)
self.assertEqual(SampleNamespace.enumItemAsDefaultValueToIntArgument(123), 123)
def testAnonymousGlobalEnums(self):
self.assertEqual(sum, 1)
def testSetEnum(self):
- event = Event(Event.ANY_EVENT)
- self.assertEqual(event.eventType(), Event.ANY_EVENT)
- event.setEventType(Event.BASIC_EVENT)
- self.assertEqual(event.eventType(), Event.BASIC_EVENT)
- event.setEventTypeByConstRef(Event.SOME_EVENT)
- self.assertEqual(event.eventType(), Event.SOME_EVENT)
- event.setEventTypeByConstPtr(Event.BASIC_EVENT)
- self.assertEqual(event.eventType(), Event.BASIC_EVENT)
+ event = Event(Event.EventType.ANY_EVENT)
+ self.assertEqual(event.eventType(), Event.EventType.ANY_EVENT)
+ event.setEventType(Event.EventType.BASIC_EVENT)
+ self.assertEqual(event.eventType(), Event.EventType.BASIC_EVENT)
+ event.setEventTypeByConstRef(Event.EventType.SOME_EVENT)
+ self.assertEqual(event.eventType(), Event.EventType.SOME_EVENT)
+ event.setEventTypeByConstPtr(Event.EventType.BASIC_EVENT)
+ self.assertEqual(event.eventType(), Event.EventType.BASIC_EVENT)
def testEnumArgumentWithDefaultValue(self):
'''Option enumArgumentWithDefaultValue(Option opt = UnixTime);'''
- self.assertEqual(SampleNamespace.enumArgumentWithDefaultValue(), SampleNamespace.UnixTime)
- self.assertEqual(SampleNamespace.enumArgumentWithDefaultValue(SampleNamespace.RandomNumber), # noqa E:501
- SampleNamespace.RandomNumber)
+ self.assertEqual(SampleNamespace.enumArgumentWithDefaultValue(),
+ SampleNamespace.Option.UnixTime)
+ self.assertEqual(SampleNamespace.enumArgumentWithDefaultValue(SampleNamespace.Option.RandomNumber), # noqa E:501
+ SampleNamespace.Option.RandomNumber)
class MyEvent(Event):
'''Overload with Enums and ints with default value'''
o = ObjectType()
- self.assertEqual(o.callWithEnum('', Event.ANY_EVENT, 9), 81)
+ self.assertEqual(o.callWithEnum('', Event.EventType.ANY_EVENT, 9), 81)
self.assertEqual(o.callWithEnum('', 9), 9)
'''Test case for operations on enums'''
def testInequalitySameObject(self):
- self.assertFalse(Event.ANY_EVENT != Event.ANY_EVENT)
+ self.assertFalse(Event.EventType.ANY_EVENT
+ != Event.EventType.ANY_EVENT)
if __name__ == '__main__':
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
def testEnumPromotedToUpperNamespace(self):
sample.UnremovedNamespace
sample.UnremovedNamespace.RemovedNamespace3_Enum
- sample.UnremovedNamespace.RemovedNamespace3_Enum_Value0
+ sample.UnremovedNamespace.RemovedNamespace3_Enum.RemovedNamespace3_Enum_Value0
sample.UnremovedNamespace.RemovedNamespace3_AnonymousEnum_Value0
def testNestedFunctionFromRemovedNamespace(self):
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Simple event loop dispatcher test.'''
objs = [ObjectType(), NoOverride(), Override()]
evaluated = ObjectType.processEvent(objs,
- Event(Event.BASIC_EVENT))
+ Event(Event.EventType.BASIC_EVENT))
self.assertEqual(evaluated, 3)
self.assertTrue(objs[2].called)
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
from random import random
thread.start()
evaluated = ObjectType.processEvent(objs,
- Event(Event.BASIC_EVENT))
+ Event(Event.EventType.BASIC_EVENT))
thread.join()
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
def testAnd(self):
- f1 = Data(Data.Name, "joe")
+ f1 = Data(Data.Field.Name, "joe")
f2 = Union()
inter = f1 & f2
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
''' Test case for a class that holds a unknown handle object.
Test case for BUG #1105.
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for __hash__'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for inplicit converting C++ numeric types.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for implicit conversions'''
def testImplicitConversions(self):
'''Test if overloaded function call decisor takes implicit conversions into account.'''
ic = ImplicitConv.implicitConvCommon(ImplicitConv())
- self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorNone)
+ self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorEnum.CtorNone)
ic = ImplicitConv.implicitConvCommon(3)
- self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorOne)
+ self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorEnum.CtorOne)
self.assertEqual(ic.objId(), 3)
- ic = ImplicitConv.implicitConvCommon(ImplicitConv.CtorThree)
- self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorThree)
+ ic = ImplicitConv.implicitConvCommon(ImplicitConv.CtorEnum.CtorThree)
+ self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorEnum.CtorThree)
obj = ObjectType()
ic = ImplicitConv.implicitConvCommon(obj)
- self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorObjectTypeReference)
+ self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorEnum.CtorObjectTypeReference)
ic = ImplicitConv.implicitConvCommon(42.42)
self.assertEqual(ic.value(), 42.42)
ic = ImplicitConv(None)
- self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorPrimitiveType)
+ self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorEnum.CtorPrimitiveType)
if __name__ == '__main__':
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for finding scope in cases involving inheritance.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for std::list container conversions'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
'''IntList constructor receives no parameter.'''
il = IntList()
self.assertEqual(len(il), 0)
- self.assertEqual(il.constructorUsed(), IntList.NoParamsCtor)
+ self.assertEqual(il.constructorUsed(), IntList.CtorEnum.NoParamsCtor)
def testIntListCtor_int(self):
'''IntList constructor receives an integer.'''
il = IntList(value)
self.assertEqual(len(il), 1)
self.assertEqual(il[0], value)
- self.assertEqual(il.constructorUsed(), IntList.IntCtor)
+ self.assertEqual(il.constructorUsed(), IntList.CtorEnum.IntCtor)
def testIntListCtor_IntList(self):
'''IntList constructor receives an IntList object.'''
self.assertEqual(len(il1), len(il2))
for i in range(len(il1)):
self.assertEqual(il1[i], il2[i])
- self.assertEqual(il2.constructorUsed(), IntList.CopyCtor)
+ self.assertEqual(il2.constructorUsed(), IntList.CtorEnum.CopyCtor)
def testIntListCtor_ListOfInts(self):
'''IntList constructor receives an integer list.'''
self.assertEqual(len(il), len(ints))
for i in range(len(il)):
self.assertEqual(il[i], ints[i])
- self.assertEqual(il.constructorUsed(), IntList.ListOfIntCtor)
+ self.assertEqual(il.constructorUsed(), IntList.CtorEnum.ListOfIntCtor)
def testIntListAttributeTypeCheck(self):
'''Attribute values to IntList.'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for returning invalid types in a virtual function'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for std::list container conversions'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Simple test with a blocking C++ method that should allow python
threads to run.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for std::map container conversions'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
pass
-class A(object):
+class A:
__metaclass__ = MetaA
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for virtual methods in multiple inheritance scenarios'''
def testCpp(self):
'''C++ calling C++ virtual method in multiple inheritance scenario'''
obj = ImplementsNone()
- self.assertTrue(ObjectType.processEvent([obj], Event(Event.BASIC_EVENT)))
+ self.assertTrue(ObjectType.processEvent([obj], Event(Event.EventType.BASIC_EVENT)))
self.assertRaises(AttributeError, getattr, obj, 'event_processed')
self.assertEqual(obj.callSum0(1, 2, 3), 6)
def testEvent(self):
'''C++ calling Python reimplementation of virtual in multiple inheritance'''
obj = ImplementsBoth()
- self.assertTrue(ObjectType.processEvent([obj], Event(Event.BASIC_EVENT)))
+ self.assertTrue(ObjectType.processEvent([obj], Event(Event.EventType.BASIC_EVENT)))
self.assertTrue(obj.event_processed)
self.assertEqual(obj.callSum1(1, 2, 3), 12)
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for multiple inheritance in mixed Python/C++ scenarios'''
from sample import ObjectType
-class Base(object):
+class Base:
'''Base Python class'''
def __init__(self):
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for objects that keep references to other object without owning them
(e.g. model/view relationships).'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for method modifications performed as described on type system. '''
def testOverloadedMethodModifications(self):
'''Tests modifications to an overloaded method'''
# overloaded(int, bool[removed], int, double)
- self.assertEqual(self.mods.overloaded(1, 2, 3.1), Modifications.Overloaded_ibid)
+ self.assertEqual(self.mods.overloaded(1, 2, 3.1),
+ Modifications.OverloadedModFunc.Overloaded_ibid)
# overloaded(int, bool, int[removed,default=321], int)
- self.assertEqual(self.mods.overloaded(1, True, 2), Modifications.Overloaded_ibii)
+ self.assertEqual(self.mods.overloaded(1, True, 2),
+ Modifications.OverloadedModFunc.Overloaded_ibii)
# the others weren't modified
- self.assertEqual(self.mods.overloaded(1, True, 2, False), Modifications.Overloaded_ibib)
+ self.assertEqual(self.mods.overloaded(1, True, 2, False),
+ Modifications.OverloadedModFunc.Overloaded_ibib)
self.assertEqual(self.mods.overloaded(1, False, 2, Point(3, 4)),
- Modifications.Overloaded_ibiP)
+ Modifications.OverloadedModFunc.Overloaded_ibiP)
self.assertRaises(TypeError, self.mods.overloaded, 1, True, Point(2, 3), Point(4, 5))
self.assertEqual(self.mods.over(1, True, Point(2, 3), Point(4, 5)),
- Modifications.Overloaded_ibPP)
+ Modifications.OverloadedModFunc.Overloaded_ibPP)
def testPointArrayModification(self):
points = (Point(1, 1), Point(2, 2))
# qualified by the enum scope.
modifications = Modifications()
modifications.setEnumValue()
- self.assertEqual(modifications.enumValue(), Modifications.TestEnumValue2)
+ self.assertEqual(modifications.enumValue(), Modifications.TestEnum.TestEnumValue2)
def testSetGetAttro(self):
modifications = Modifications()
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests cases for ConstructorWithModifiedArgument class.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for modified virtual methods.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for multiple inheritance'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for multiple inheritance'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for std::map container conversions'''
def testInlineNamespaces(self):
cls = SampleNamespace.ClassWithinInlineNamespace()
- cls.setValue(SampleNamespace.EWIN_Value1)
- self.assertEqual(cls.value(), SampleNamespace.EWIN_Value1)
+ cls.setValue(SampleNamespace.EnumWithinInlineNamespace.EWIN_Value1)
+ self.assertEqual(cls.value(), SampleNamespace.EnumWithinInlineNamespace.EWIN_Value1)
if __name__ == '__main__':
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for ...'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
hasNumPy = False
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests ObjectType class of object-type with privates copy constructor and = operator.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests cases for ObjectTypeLayout class.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for OddBool user's primitive type conversion.'''
self.assertEqual(t1, t2)
def testSpaceshipOperator(self):
- if not SpaceshipComparisonTester.HasSpaceshipOperator:
+ if not SpaceshipComparisonTester.Enabled.HasSpaceshipOperator:
print("Skipping Spaceship Operator test")
return
t1 = SpaceshipComparisonTester(42)
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for overflowing C++ numeric types.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for overload sorting'''
ImplicitTarget, SortedOverload)
-class Dummy(object):
+class Dummy:
pass
class EnumOverIntSorting(unittest.TestCase):
def testEnumOverInt(self):
- ic = ImplicitConv(ImplicitConv.CtorTwo)
- self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorTwo)
+ ic = ImplicitConv(ImplicitConv.CtorEnum.CtorTwo)
+ self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorEnum.CtorTwo)
class TestCustomOverloadSequence(unittest.TestCase):
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for Overload class'''
def testOverloadMethod0(self):
'''Check overloaded method call for signature "overloaded()".'''
overload = Overload()
- self.assertEqual(overload.overloaded(), Overload.Function0)
+ self.assertEqual(overload.overloaded(), Overload.FunctionEnum.Function0)
def testOverloadMethod1(self):
'''Check overloaded method call for signature "overloaded(Size*)".'''
overload = Overload()
size = Size()
- self.assertEqual(overload.overloaded(size), Overload.Function1)
+ self.assertEqual(overload.overloaded(size), Overload.FunctionEnum.Function1)
def testOverloadMethod2(self):
'''Check overloaded method call for signature "overloaded(Point*, ParamEnum)".'''
overload = Overload()
point = Point()
- self.assertEqual(overload.overloaded(point, Overload.Param1), Overload.Function2)
+ self.assertEqual(overload.overloaded(point, Overload.ParamEnum.Param1),
+ Overload.FunctionEnum.Function2)
def testOverloadMethod3(self):
'''Check overloaded method call for signature "overloaded(const Point&)".'''
overload = Overload()
point = Point()
- self.assertEqual(overload.overloaded(point), Overload.Function3)
+ self.assertEqual(overload.overloaded(point), Overload.FunctionEnum.Function3)
def testDifferentReturnTypes(self):
'''Check method calls for overloads with different return types.'''
overload = Overload()
self.assertEqual(overload.differentReturnTypes(), None)
- self.assertEqual(overload.differentReturnTypes(Overload.Param1), None)
- self.assertEqual(overload.differentReturnTypes(Overload.Param0, 13), 13)
+ self.assertEqual(overload.differentReturnTypes(Overload.ParamEnum.Param1), None)
+ self.assertEqual(overload.differentReturnTypes(Overload.ParamEnum.Param0, 13), 13)
def testIntOverloads(self):
overload = Overload()
def testIntDoubleOverloads(self):
overload = Overload()
- self.assertEqual(overload.intDoubleOverloads(1, 2), Overload.Function0)
- self.assertEqual(overload.intDoubleOverloads(1, 2.0), Overload.Function0)
- self.assertEqual(overload.intDoubleOverloads(1.0, 2), Overload.Function1)
- self.assertEqual(overload.intDoubleOverloads(1.0, 2.0), Overload.Function1)
+ self.assertEqual(overload.intDoubleOverloads(1, 2), Overload.FunctionEnum.Function0)
+ self.assertEqual(overload.intDoubleOverloads(1, 2.0), Overload.FunctionEnum.Function0)
+ self.assertEqual(overload.intDoubleOverloads(1.0, 2), Overload.FunctionEnum.Function1)
+ self.assertEqual(overload.intDoubleOverloads(1.0, 2.0), Overload.FunctionEnum.Function1)
def testWrapperIntIntOverloads(self):
overload = Overload()
- self.assertEqual(overload.wrapperIntIntOverloads(Point(), 1, 2), Overload.Function0)
- self.assertEqual(overload.wrapperIntIntOverloads(Polygon(), 1, 2), Overload.Function1)
+ self.assertEqual(overload.wrapperIntIntOverloads(Point(), 1, 2),
+ Overload.FunctionEnum.Function0)
+ self.assertEqual(overload.wrapperIntIntOverloads(Polygon(), 1, 2),
+ Overload.FunctionEnum.Function1)
def testDrawTextPointAndStr(self):
overload = Overload()
- self.assertEqual(overload.drawText(Point(), Str()), Overload.Function0)
- self.assertEqual(overload.drawText(Point(), ''), Overload.Function0)
- self.assertEqual(overload.drawText(PointF(), Str()), Overload.Function1)
- self.assertEqual(overload.drawText(PointF(), ''), Overload.Function1)
+ self.assertEqual(overload.drawText(Point(), Str()), Overload.FunctionEnum.Function0)
+ self.assertEqual(overload.drawText(Point(), ''), Overload.FunctionEnum.Function0)
+ self.assertEqual(overload.drawText(PointF(), Str()), Overload.FunctionEnum.Function1)
+ self.assertEqual(overload.drawText(PointF(), ''), Overload.FunctionEnum.Function1)
def testDrawTextRectIntStr(self):
overload = Overload()
- self.assertEqual(overload.drawText(Rect(), 1, Str()), Overload.Function2)
- self.assertEqual(overload.drawText(Rect(), 1, ''), Overload.Function2)
- self.assertEqual(overload.drawText(RectF(), 1, Str()), Overload.Function3)
- self.assertEqual(overload.drawText(RectF(), 1, ''), Overload.Function3)
+ self.assertEqual(overload.drawText(Rect(), 1, Str()), Overload.FunctionEnum.Function2)
+ self.assertEqual(overload.drawText(Rect(), 1, ''), Overload.FunctionEnum.Function2)
+ self.assertEqual(overload.drawText(RectF(), 1, Str()), Overload.FunctionEnum.Function3)
+ self.assertEqual(overload.drawText(RectF(), 1, ''), Overload.FunctionEnum.Function3)
def testDrawTextRectFStrEcho(self):
overload = Overload()
- self.assertEqual(overload.drawText(RectF(), Str()), Overload.Function4)
- self.assertEqual(overload.drawText(RectF(), ''), Overload.Function4)
- self.assertEqual(overload.drawText(RectF(), Str(), Echo()), Overload.Function4)
- self.assertEqual(overload.drawText(RectF(), '', Echo()), Overload.Function4)
- self.assertEqual(overload.drawText(Rect(), Str()), Overload.Function4)
- self.assertEqual(overload.drawText(Rect(), ''), Overload.Function4)
- self.assertEqual(overload.drawText(Rect(), Str(), Echo()), Overload.Function4)
- self.assertEqual(overload.drawText(Rect(), '', Echo()), Overload.Function4)
+ self.assertEqual(overload.drawText(RectF(), Str()), Overload.FunctionEnum.Function4)
+ self.assertEqual(overload.drawText(RectF(), ''), Overload.FunctionEnum.Function4)
+ self.assertEqual(overload.drawText(RectF(), Str(), Echo()), Overload.FunctionEnum.Function4)
+ self.assertEqual(overload.drawText(RectF(), '', Echo()), Overload.FunctionEnum.Function4)
+ self.assertEqual(overload.drawText(Rect(), Str()), Overload.FunctionEnum.Function4)
+ self.assertEqual(overload.drawText(Rect(), ''), Overload.FunctionEnum.Function4)
+ self.assertEqual(overload.drawText(Rect(), Str(), Echo()), Overload.FunctionEnum.Function4)
+ self.assertEqual(overload.drawText(Rect(), '', Echo()), Overload.FunctionEnum.Function4)
def testDrawTextIntIntStr(self):
overload = Overload()
- self.assertEqual(overload.drawText(1, 2, Str()), Overload.Function5)
- self.assertEqual(overload.drawText(1, 2, ''), Overload.Function5)
+ self.assertEqual(overload.drawText(1, 2, Str()), Overload.FunctionEnum.Function5)
+ self.assertEqual(overload.drawText(1, 2, ''), Overload.FunctionEnum.Function5)
def testDrawTextIntIntIntIntStr(self):
overload = Overload()
- self.assertEqual(overload.drawText(1, 2, 3, 4, 5, Str()), Overload.Function6)
- self.assertEqual(overload.drawText(1, 2, 3, 4, 5, ''), Overload.Function6)
+ self.assertEqual(overload.drawText(1, 2, 3, 4, 5, Str()), Overload.FunctionEnum.Function6)
+ self.assertEqual(overload.drawText(1, 2, 3, 4, 5, ''), Overload.FunctionEnum.Function6)
def testDrawText2IntIntIntIntStr(self):
overload = Overload()
- self.assertEqual(overload.drawText2(1, 2, 3, 4, 5, Str()), Overload.Function6)
- self.assertEqual(overload.drawText2(1, 2, 3, 4, 5, ''), Overload.Function6)
- self.assertEqual(overload.drawText2(1, 2, 3, 4, 5), Overload.Function6)
- self.assertEqual(overload.drawText2(1, 2, 3, 4), Overload.Function6)
- self.assertEqual(overload.drawText2(1, 2, 3), Overload.Function6)
+ self.assertEqual(overload.drawText2(1, 2, 3, 4, 5, Str()), Overload.FunctionEnum.Function6)
+ self.assertEqual(overload.drawText2(1, 2, 3, 4, 5, ''), Overload.FunctionEnum.Function6)
+ self.assertEqual(overload.drawText2(1, 2, 3, 4, 5), Overload.FunctionEnum.Function6)
+ self.assertEqual(overload.drawText2(1, 2, 3, 4), Overload.FunctionEnum.Function6)
+ self.assertEqual(overload.drawText2(1, 2, 3), Overload.FunctionEnum.Function6)
def testDrawText3(self):
overload = Overload()
- self.assertEqual(overload.drawText3(Str(), Str(), Str()), Overload.Function0)
- self.assertEqual(overload.drawText3('', '', ''), Overload.Function0)
- self.assertEqual(overload.drawText3(1, 2, 3, 4, 5), Overload.Function1)
- self.assertEqual(overload.drawText3(1, 2, 3, 4, 5), Overload.Function1)
+ self.assertEqual(overload.drawText3(Str(), Str(), Str()), Overload.FunctionEnum.Function0)
+ self.assertEqual(overload.drawText3('', '', ''), Overload.FunctionEnum.Function0)
+ self.assertEqual(overload.drawText3(1, 2, 3, 4, 5), Overload.FunctionEnum.Function1)
+ self.assertEqual(overload.drawText3(1, 2, 3, 4, 5), Overload.FunctionEnum.Function1)
def testDrawText3Exception(self):
overload = Overload()
def testDrawText4(self):
overload = Overload()
- self.assertEqual(overload.drawText4(1, 2, 3), Overload.Function0)
- self.assertEqual(overload.drawText4(1, 2, 3, 4, 5), Overload.Function1)
+ self.assertEqual(overload.drawText4(1, 2, 3), Overload.FunctionEnum.Function0)
+ self.assertEqual(overload.drawText4(1, 2, 3, 4, 5), Overload.FunctionEnum.Function1)
def testAcceptSequence(self):
# Overload.acceptSequence()
overload = Overload()
- self.assertEqual(overload.acceptSequence(), Overload.Function0)
+ self.assertEqual(overload.acceptSequence(), Overload.FunctionEnum.Function0)
def testAcceptSequenceIntInt(self):
# Overload.acceptSequence(int,int)
overload = Overload()
- self.assertEqual(overload.acceptSequence(1, 2), Overload.Function1)
+ self.assertEqual(overload.acceptSequence(1, 2), Overload.FunctionEnum.Function1)
def testAcceptSequenceStrParamEnum(self):
# Overload.acceptSequence(Str,Overload::ParamEnum)
overload = Overload()
- self.assertEqual(overload.acceptSequence(''), Overload.Function2)
- self.assertEqual(overload.acceptSequence('', Overload.Param0), Overload.Function2)
- self.assertEqual(overload.acceptSequence(Str('')), Overload.Function2)
- self.assertEqual(overload.acceptSequence(Str(''), Overload.Param0), Overload.Function2)
+ self.assertEqual(overload.acceptSequence(''), Overload.FunctionEnum.Function2)
+ self.assertEqual(overload.acceptSequence('', Overload.ParamEnum.Param0),
+ Overload.FunctionEnum.Function2)
+ self.assertEqual(overload.acceptSequence(Str('')), Overload.FunctionEnum.Function2)
+ self.assertEqual(overload.acceptSequence(Str(''), Overload.ParamEnum.Param0),
+ Overload.FunctionEnum.Function2)
def testAcceptSequenceSize(self):
# Overload.acceptSequence(Size)
overload = Overload()
- self.assertEqual(overload.acceptSequence(Size()), Overload.Function3)
+ self.assertEqual(overload.acceptSequence(Size()), Overload.FunctionEnum.Function3)
def testAcceptSequenceStringList(self):
# Overload.acceptSequence(const char**)
overload = Overload()
strings = ['line 1', 'line 2']
- self.assertEqual(overload.acceptSequence(strings), Overload.Function4)
+ self.assertEqual(overload.acceptSequence(strings), Overload.FunctionEnum.Function4)
args = (['line 1', 2], )
result = raisesWithErrorMessage(overload.acceptSequence, args,
TypeError, 'The argument must be a sequence of strings.')
# Overload.acceptSequence(void*)
overload = Overload()
- class Foo(object):
+ class Foo:
pass
foo = Foo()
- self.assertEqual(overload.acceptSequence(foo), Overload.Function5)
+ self.assertEqual(overload.acceptSequence(foo), Overload.FunctionEnum.Function5)
if __name__ == '__main__':
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
def testNoArgument(self):
overload = Overload()
- self.assertEqual(overload.strBufferOverloads(), Overload.Function2)
+ self.assertEqual(overload.strBufferOverloads(), Overload.FunctionEnum.Function2)
def testStrArgument(self):
overload = Overload()
- self.assertEqual(overload.strBufferOverloads(Str('')), Overload.Function0)
- self.assertEqual(overload.strBufferOverloads(Str(''), ''), Overload.Function0)
- self.assertEqual(overload.strBufferOverloads(Str(''), '', False), Overload.Function0)
+ self.assertEqual(overload.strBufferOverloads(Str('')), Overload.FunctionEnum.Function0)
+ self.assertEqual(overload.strBufferOverloads(Str(''), ''), Overload.FunctionEnum.Function0)
+ self.assertEqual(overload.strBufferOverloads(Str(''), '', False),
+ Overload.FunctionEnum.Function0)
def testStringArgumentAsStr(self):
overload = Overload()
- self.assertEqual(overload.strBufferOverloads('', ''), Overload.Function0)
- self.assertEqual(overload.strBufferOverloads('', '', False), Overload.Function0)
+ self.assertEqual(overload.strBufferOverloads('', ''), Overload.FunctionEnum.Function0)
+ self.assertEqual(overload.strBufferOverloads('', '', False),
+ Overload.FunctionEnum.Function0)
def testStringArgumentAsBuffer(self):
overload = Overload()
- self.assertEqual(overload.strBufferOverloads(bytes('', "UTF-8"), 0), Overload.Function1)
+ self.assertEqual(overload.strBufferOverloads(bytes('', "UTF-8"), 0),
+ Overload.FunctionEnum.Function1)
def testBufferArgument(self):
overload = Overload()
- self.assertEqual(overload.strBufferOverloads(bytes('', "UTF-8"), 0), Overload.Function1)
+ self.assertEqual(overload.strBufferOverloads(bytes('', "UTF-8"), 0),
+ Overload.FunctionEnum.Function1)
if __name__ == '__main__':
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Wrapper validity tests for arguments.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests for destroy a child object in C++'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests for deleting a child object in python'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests for destroying the parent'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Ownership tests for cases of invalidation of Python wrapper after use.'''
'''In ObjectType.event(Event*) the wrapper object created for Event
must me marked as invalid after the method is called.'''
eot = ExtObjectType()
- eot.causeEvent(Event.SOME_EVENT)
- self.assertEqual(eot.type_of_last_event, Event.SOME_EVENT)
+ eot.causeEvent(Event.EventType.SOME_EVENT)
+ self.assertEqual(eot.type_of_last_event, Event.EventType.SOME_EVENT)
self.assertRaises(RuntimeError, eot.last_event.eventType)
def testObjectInvalidatedAfterUseAsParameter(self):
'''Tries to use wrapper invalidated after use as a parameter to another method.'''
eot = ExtObjectType()
ot = ObjectType()
- eot.causeEvent(Event.ANY_EVENT)
- self.assertEqual(eot.type_of_last_event, Event.ANY_EVENT)
+ eot.causeEvent(Event.EventType.ANY_EVENT)
+ self.assertEqual(eot.type_of_last_event, Event.EventType.ANY_EVENT)
self.assertRaises(RuntimeError, ot.event, eot.last_event)
def testit(self):
obj = MyObjectType()
- obj.causeEvent(Event.BASIC_EVENT)
+ obj.causeEvent(Event.EventType.BASIC_EVENT)
self.assertFalse(obj.fail)
def testInvalidateAfterUseInDerived(self):
'''Invalidate was failing in a derived C++ class that also inherited
other base classes'''
eot = ExtObjectTypeDerived()
- eot.causeEvent(Event.SOME_EVENT)
- self.assertEqual(eot.type_of_last_event, Event.SOME_EVENT)
+ eot.causeEvent(Event.EventType.SOME_EVENT)
+ self.assertEqual(eot.type_of_last_event, Event.EventType.SOME_EVENT)
self.assertRaises(RuntimeError, eot.last_event.eventType)
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests for invalidating a C++ created child that was already on the care of a parent.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''The BlackBox class has cases of ownership transference between Python and C++.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests for invalidating a parent of other objects.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests for object reparenting.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''The BlackBox class has cases of ownership transference between C++ and Python.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for std::pair container conversions'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for <add-function> with const char* as argument'''
self.assertEqual(pen.ctorType(), Pen.EmptyCtor)
def testPenWithEnumConstructor(self):
- pen = Pen(SampleNamespace.RandomNumber)
+ pen = Pen(SampleNamespace.Option.RandomNumber)
self.assertEqual(pen.ctorType(), Pen.EnumCtor)
def testPenWithColorConstructor(self):
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for Point class'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for a class that holds an arbitraty pointer and is modified to hold an PyObject.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
"""
pointerprimitivetype_test.py
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for PointF class'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for a class with only a private constructor.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for a class with a private destructor.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for protected methods.'''
ProtectedEnumClass.__init__(self)
def protectedEnumMethod(self, value):
- if value == ProtectedEnumClass.ProtectedItem0:
- return ProtectedEnumClass.ProtectedItem1
- return ProtectedEnumClass.ProtectedItem0
+ if value == ProtectedEnumClass.ProtectedEnum.ProtectedItem0:
+ return ProtectedEnumClass.ProtectedEnum.ProtectedItem1
+ return ProtectedEnumClass.ProtectedEnum.ProtectedItem0
def publicEnumMethod(self, value):
- if value == ProtectedEnumClass.PublicItem0:
- return ProtectedEnumClass.PublicItem1
- return ProtectedEnumClass.PublicItem0
+ if value == ProtectedEnumClass.PublicEnum.PublicItem0:
+ return ProtectedEnumClass.PublicEnum.PublicItem1
+ return ProtectedEnumClass.PublicEnum.PublicItem0
class ProtectedEnumTest(unittest.TestCase):
'''Calls protected method with protected enum argument.'''
obj = ProtectedEnumClass()
- self.assertEqual(type(ProtectedEnumClass.ProtectedItem0), ProtectedEnumClass.ProtectedEnum)
+ self.assertEqual(type(ProtectedEnumClass.ProtectedEnum.ProtectedItem0),
+ ProtectedEnumClass.ProtectedEnum)
- self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem0),
- ProtectedEnumClass.ProtectedItem0)
- self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem1),
- ProtectedEnumClass.ProtectedItem1)
- self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem0),
- ProtectedEnumClass.ProtectedItem0)
- self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem1),
- ProtectedEnumClass.ProtectedItem1)
+ self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedEnum.ProtectedItem0),
+ ProtectedEnumClass.ProtectedEnum.ProtectedItem0)
+ self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedEnum.ProtectedItem1),
+ ProtectedEnumClass.ProtectedEnum.ProtectedItem1)
+ self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedEnum.ProtectedItem0), # noqa: E501
+ ProtectedEnumClass.ProtectedEnum.ProtectedItem0)
+ self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedEnum.ProtectedItem1), # noqa: E501
+ ProtectedEnumClass.ProtectedEnum.ProtectedItem1)
def testProtectedMethodWithPublicEnumArgument(self):
'''Calls protected method with public enum argument.'''
obj = ProtectedEnumClass()
- self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem0),
- ProtectedEnumClass.PublicItem0)
- self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem1),
- ProtectedEnumClass.PublicItem1)
+ self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicEnum.PublicItem0),
+ ProtectedEnumClass.PublicEnum.PublicItem0)
+ self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicEnum.PublicItem1),
+ ProtectedEnumClass.PublicEnum.PublicItem1)
- self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem0),
- ProtectedEnumClass.PublicItem0)
- self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem1),
- ProtectedEnumClass.PublicItem1)
+ self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicEnum.PublicItem0),
+ ProtectedEnumClass.PublicEnum.PublicItem0)
+ self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicEnum.PublicItem1),
+ ProtectedEnumClass.PublicEnum.PublicItem1)
def testOverriddenProtectedMethodWithProtectedEnumArgument(self):
'''Calls overridden protected method with protected enum argument.'''
obj = ExtendedProtectedEnumClass()
- self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem0),
- ProtectedEnumClass.ProtectedItem1)
- self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedItem1),
- ProtectedEnumClass.ProtectedItem0)
+ self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedEnum.ProtectedItem0),
+ ProtectedEnumClass.ProtectedEnum.ProtectedItem1)
+ self.assertEqual(obj.protectedEnumMethod(ProtectedEnumClass.ProtectedEnum.ProtectedItem1),
+ ProtectedEnumClass.ProtectedEnum.ProtectedItem0)
- self.assertEqual(ProtectedEnumClass.protectedEnumMethod(obj, ProtectedEnumClass.ProtectedItem0), # noqa: E501
- ProtectedEnumClass.ProtectedItem0)
+ self.assertEqual(ProtectedEnumClass.protectedEnumMethod(obj, ProtectedEnumClass.ProtectedEnum.ProtectedItem0), # noqa: E501
+ ProtectedEnumClass.ProtectedEnum.ProtectedItem0)
self.assertEqual(ProtectedEnumClass.protectedEnumMethod(obj,
- ProtectedEnumClass.ProtectedItem1), ProtectedEnumClass.ProtectedItem1)
+ ProtectedEnumClass.ProtectedEnum.ProtectedItem1), ProtectedEnumClass.ProtectedEnum.ProtectedItem1) # noqa: E501
- self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem0),
- ProtectedEnumClass.ProtectedItem1)
- self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedItem1),
- ProtectedEnumClass.ProtectedItem0)
+ self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedEnum.ProtectedItem0), # noqa: E501
+ ProtectedEnumClass.ProtectedEnum.ProtectedItem1)
+ self.assertEqual(obj.callProtectedEnumMethod(ProtectedEnumClass.ProtectedEnum.ProtectedItem1), # noqa: E501
+ ProtectedEnumClass.ProtectedEnum.ProtectedItem0)
def testOverriddenProtectedMethodWithPublicEnumArgument(self):
'''Calls overridden protected method with public enum argument.'''
obj = ExtendedProtectedEnumClass()
- self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem0),
- ProtectedEnumClass.PublicItem1)
- self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicItem1),
- ProtectedEnumClass.PublicItem0)
+ self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicEnum.PublicItem0),
+ ProtectedEnumClass.PublicEnum.PublicItem1)
+ self.assertEqual(obj.publicEnumMethod(ProtectedEnumClass.PublicEnum.PublicItem1),
+ ProtectedEnumClass.PublicEnum.PublicItem0)
- self.assertEqual(ProtectedEnumClass.publicEnumMethod(obj, ProtectedEnumClass.PublicItem0),
- ProtectedEnumClass.PublicItem0)
- self.assertEqual(ProtectedEnumClass.publicEnumMethod(obj, ProtectedEnumClass.PublicItem1),
- ProtectedEnumClass.PublicItem1)
+ self.assertEqual(ProtectedEnumClass.publicEnumMethod(obj, ProtectedEnumClass.PublicEnum.PublicItem0), # noqa: E501
+ ProtectedEnumClass.PublicEnum.PublicItem0)
+ self.assertEqual(ProtectedEnumClass.publicEnumMethod(obj, ProtectedEnumClass.PublicEnum.PublicItem1), # noqa: E501
+ ProtectedEnumClass.PublicEnum.PublicItem1)
- self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem0),
- ProtectedEnumClass.PublicItem1)
- self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicItem1),
- ProtectedEnumClass.PublicItem0)
+ self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicEnum.PublicItem0),
+ ProtectedEnumClass.PublicEnum.PublicItem1)
+ self.assertEqual(obj.callPublicEnumMethod(ProtectedEnumClass.PublicEnum.PublicItem1),
+ ProtectedEnumClass.PublicEnum.PublicItem0)
class ProtectedPropertyTest(unittest.TestCase):
def testProtectedEnumProperty(self):
'''Writes and reads a protected enum property.'''
- self.obj.protectedEnumProperty = Event.SOME_EVENT
- self.assertEqual(self.obj.protectedEnumProperty, Event.SOME_EVENT)
+ self.obj.protectedEnumProperty = Event.EventType.SOME_EVENT
+ self.assertEqual(self.obj.protectedEnumProperty, Event.EventType.SOME_EVENT)
def testProtectedValueTypeProperty(self):
'''Writes and reads a protected value type property.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for definition of __str__ method.'''
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
#!/usr/bin/env python
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for a function that could receive a NULL pointer in a '[const] char*' parameter.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for methods that receive references to objects.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for a reference to pointer argument type.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for renaming using target-lang-name attribute.'''
rename_user = RenamedUser()
rename_user.useRenamedValue(renamed_value)
actual_signature = str(get_signature(rename_user.useRenamedValue))
- self.assertTrue(re.match(r"^\(self,\s*?v:\s*?sample.RenamedValue\)\s*?->\s*?None$",
+ self.assertTrue(re.match(r"^\(self,\s*?v:\s*?sample.RenamedValue(,\s*?/)?\)\s*?->\s*?None$",
actual_signature))
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for functions that could return a NULL pointer.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for libsample bindings module'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for SimpleFile class'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for operator overloads on Size class'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for snake case generation'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for overloads involving static and non-static versions of a method.'''
#!/usr/bin/env python
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for StdComplex class'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for a method that receives a reference to class that is implicitly
convertible from a Python native type.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for StrList class that inherits from std::list<Str>.'''
'''StrList constructor receives no parameter.'''
sl = StrList()
self.assertEqual(len(sl), 0)
- self.assertEqual(sl.constructorUsed(), StrList.NoParamsCtor)
+ self.assertEqual(sl.constructorUsed(), StrList.CtorEnum.NoParamsCtor)
def testStrListCtor_Str(self):
'''StrList constructor receives a Str object.'''
sl = StrList(s)
self.assertEqual(len(sl), 1)
self.assertEqual(sl[0], s)
- self.assertEqual(sl.constructorUsed(), StrList.StrCtor)
+ self.assertEqual(sl.constructorUsed(), StrList.CtorEnum.StrCtor)
def testStrListCtor_PythonString(self):
'''StrList constructor receives a Python string.'''
sl = StrList(s)
self.assertEqual(len(sl), 1)
self.assertEqual(sl[0], s)
- self.assertEqual(sl.constructorUsed(), StrList.StrCtor)
+ self.assertEqual(sl.constructorUsed(), StrList.CtorEnum.StrCtor)
def testStrListCtor_StrList(self):
'''StrList constructor receives a StrList object.'''
sl2 = StrList(sl1)
#self.assertEqual(len(sl1), len(sl2))
#self.assertEqual(sl1, sl2)
- self.assertEqual(sl2.constructorUsed(), StrList.CopyCtor)
+ self.assertEqual(sl2.constructorUsed(), StrList.CtorEnum.CopyCtor)
def testStrListCtor_ListOfStrs(self):
'''StrList constructor receives a Python list of Str objects.'''
sl = StrList(strs)
self.assertEqual(len(sl), len(strs))
self.assertEqual(sl, strs)
- self.assertEqual(sl.constructorUsed(), StrList.ListOfStrCtor)
+ self.assertEqual(sl.constructorUsed(), StrList.CtorEnum.ListOfStrCtor)
def testStrListCtor_MixedListOfStrsAndPythonStrings(self):
'''StrList constructor receives a Python list of mixed Str objects and Python strings.'''
sl = StrList(strs)
self.assertEqual(len(sl), len(strs))
self.assertEqual(sl, strs)
- self.assertEqual(sl.constructorUsed(), StrList.ListOfStrCtor)
+ self.assertEqual(sl.constructorUsed(), StrList.CtorEnum.ListOfStrCtor)
def testCompareStrListWithTupleOfStrs(self):
'''Compares StrList with a Python tuple of Str objects.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
class TemplateInheritingClassTest(unittest.TestCase):
def testClassBasics(self):
- self.assertEqual(Photon.ValueIdentity.classType(), Photon.IdentityType)
- self.assertEqual(Photon.ValueDuplicator.classType(), Photon.DuplicatorType)
+ self.assertEqual(Photon.ValueIdentity.classType(), Photon.ClassType.IdentityType)
+ self.assertEqual(Photon.ValueDuplicator.classType(), Photon.ClassType.DuplicatorType)
def testInstanceBasics(self):
value = 123
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for constructor and method signature decisor on Time class.'''
'''Method without parameters: Time.somethingCompletelyDifferent()'''
time = Time()
result = time.somethingCompletelyDifferent()
- self.assertEqual(result, Time.ZeroArgs)
+ self.assertEqual(result, Time.NumArgs.ZeroArgs)
def testMethodWithAllParamers(self):
'''Method with all parameters:
time = Time()
obj = ObjectType()
result = time.somethingCompletelyDifferent(1, 2, ImplicitConv(2), obj)
- self.assertEqual(result, Time.FourArgs)
+ self.assertEqual(result, Time.NumArgs.FourArgs)
def testMethodWithThreeParamers(self):
'''Method with 3 parameters: Time.somethingCompletelyDifferent(...)'''
time = Time()
- result = time.somethingCompletelyDifferent(1, 2, ImplicitConv(ImplicitConv.CtorOne))
- self.assertEqual(result, Time.ThreeArgs)
+ result = time.somethingCompletelyDifferent(1, 2,
+ ImplicitConv(ImplicitConv.CtorEnum.CtorOne))
+ self.assertEqual(result, Time.NumArgs.ThreeArgs)
def testMethodWithTwoParamers(self):
'''Method with 2 parameters: Time.somethingCompletelyDifferent(...)'''
time = Time()
result = time.somethingCompletelyDifferent(1, 2)
- self.assertEqual(result, Time.TwoArgs)
+ self.assertEqual(result, Time.NumArgs.TwoArgs)
def testMethodWithThreeParamersAndImplicitConversion(self):
'''Method with 3 parameters, the last one triggers an implicit conversion.'''
time = Time()
- result = time.somethingCompletelyDifferent(1, 2, ImplicitConv.CtorOne)
- self.assertEqual(result, Time.ThreeArgs)
+ result = time.somethingCompletelyDifferent(1, 2, ImplicitConv.CtorEnum.CtorOne)
+ self.assertEqual(result, Time.NumArgs.ThreeArgs)
# PYSIDE-1436: These tests crash at shutdown due to `assert(Not)?Equal`.
def testCompareWithPythonTime(self):
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for argument modification with more than nine arguments.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Tests various usages of the type converters.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test deallocation of type extended in Python.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
<add-conversion type="PySequence">
%OUTTYPE& list = %out;
Shiboken::AutoDecRef seq(PySequence_Fast(%in, 0));
- for (int i = 0; i < PySequence_Fast_GET_SIZE(seq.object()); i++) {
- PyObject* pyItem = PySequence_Fast_GET_ITEM(seq.object(), i);
+ for (Py_ssize_t i = 0; i < PySequence_Size(seq.object()); i++) {
+ Shiboken::AutoDecRef pyItem(PySequence_GetItem(seq.object(), i));
PStr cppItem = %CONVERTTOCPP[PStr](pyItem);
list.push_back(cppItem);
}
PyObject* result = PyTuple_New(size);
for (int i = 0; i < size; ++i) {
Shiboken::Conversions::SpecificConverter converter(typeName[i]);
- PyTuple_SET_ITEM(result, i, converter.toPython(values[i]));
+ PyTuple_SetItem(result, i, converter.toPython(values[i]));
}
return result;
}
<template name="boolptr_fix_end">
PyObject* _tuple_ = PyTuple_New(2);
- PyTuple_SET_ITEM(_tuple_, 0, %PYARG_0);
+ PyTuple_SetItem(_tuple_, 0, %PYARG_0);
PyObject* _item_ = %CONVERTTOPYTHON[bool](__ok__);
- PyTuple_SET_ITEM(_tuple_, 1, _item_);
+ PyTuple_SetItem(_tuple_, 1, _item_);
%PYARG_0 = _tuple_;
</template>
<template name="return_4_arguments_as_tuple">
%PYARG_0 = PyTuple_New(4);
- PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
- PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG2_TYPE](%2));
- PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[%ARG3_TYPE](%3));
- PyTuple_SET_ITEM(%PYARG_0, 3, %CONVERTTOPYTHON[%ARG4_TYPE](%4));
+ PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
+ PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG2_TYPE](%2));
+ PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[%ARG3_TYPE](%3));
+ PyTuple_SetItem(%PYARG_0, 3, %CONVERTTOPYTHON[%ARG4_TYPE](%4));
</template>
<template name="return_5_arguments_as_tuple">
%PYARG_0 = PyTuple_New(5);
- PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
- PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG2_TYPE](%2));
- PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[%ARG3_TYPE](%3));
- PyTuple_SET_ITEM(%PYARG_0, 3, %CONVERTTOPYTHON[%ARG4_TYPE](%4));
- PyTuple_SET_ITEM(%PYARG_0, 4, %CONVERTTOPYTHON[%ARG5_TYPE](%5));
+ PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%ARG1_TYPE](%1));
+ PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG2_TYPE](%2));
+ PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[%ARG3_TYPE](%3));
+ PyTuple_SetItem(%PYARG_0, 3, %CONVERTTOPYTHON[%ARG4_TYPE](%4));
+ PyTuple_SetItem(%PYARG_0, 4, %CONVERTTOPYTHON[%ARG5_TYPE](%5));
</template>
<template name="return_none">
<replace-type modified-type="PySequence" />
<conversion-rule class="native">
Shiboken::AutoArrayPointer<Point> %out(%1);
- for (Py_ssize_t i = 0; i < %1; ++i)
- %out[i] = %CONVERTTOCPP[Point](PySequence_Fast_GET_ITEM(%PYARG_1, i));
+ for (Py_ssize_t i = 0; i < %1; ++i) {
+ Shiboken::AutoDecRef _obj(PySequence_GetItem(%PYARG_1, i));
+ %out[i] = %CONVERTTOCPP[Point](_obj);
+ }
</conversion-rule>
</modify-argument>
</modify-function>
</template>
<template name="differenceOfPointCoordinates_returnTarget">
%PYARG_0 = PyTuple_New(2);
- PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[bool](*%2));
- PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%RETURN_TYPE](%0));
+ PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[bool](*%2));
+ PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[%RETURN_TYPE](%0));
</template>
<modify-function signature="differenceOfPointCoordinates(const Point*, bool*)">
<modify-argument index="2">
%RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(&tmpObject);
%END_ALLOW_THREADS
%PYARG_0 = PyTuple_New(2);
- PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](%0));
- PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[ObjectType*](tmpObject));
+ PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](%0));
+ PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[ObjectType*](tmpObject));
</inject-code>
</modify-function>
<modify-function signature="passOddBool(OddBool)" rename="invertBoolean">
<inject-code class="target" position="end">
%PYARG_0 = PyTuple_New(2);
PyObject* _item_ = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
- PyTuple_SET_ITEM(%PYARG_0, 0, _item_);
+ PyTuple_SetItem(%PYARG_0, 0, _item_);
_item_ = %CONVERTTOPYTHON[Str*](_str_arg_);
- PyTuple_SET_ITEM(%PYARG_0, 1, _item_);
+ PyTuple_SetItem(%PYARG_0, 1, _item_);
</inject-code>
</modify-function>
<modify-function signature="callCreateStr(const char*, Str*&)">
<inject-code class="target" position="end">
%PYARG_0 = PyTuple_New(2);
PyObject* _item_ = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
- PyTuple_SET_ITEM(%PYARG_0, 0, _item_);
+ PyTuple_SetItem(%PYARG_0, 0, _item_);
_item_ = %CONVERTTOPYTHON[Str*](_str_arg_);
- PyTuple_SET_ITEM(%PYARG_0, 1, _item_);
+ PyTuple_SetItem(%PYARG_0, 1, _item_);
</inject-code>
</modify-function>
<template name="fix_int*,int*,int*,int*">
%CPPSELF->::%TYPE::%FUNCTION_NAME(&a0, &a1, &a2, &a3);
%END_ALLOW_THREADS
%PYARG_0 = PyTuple_New(4);
- PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[int](a0));
- PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[int](a1));
- PyTuple_SET_ITEM(%PYARG_0, 2, %CONVERTTOPYTHON[int](a2));
- PyTuple_SET_ITEM(%PYARG_0, 3, %CONVERTTOPYTHON[int](a3));
+ PyTuple_SetItem(%PYARG_0, 0, %CONVERTTOPYTHON[int](a0));
+ PyTuple_SetItem(%PYARG_0, 1, %CONVERTTOPYTHON[int](a1));
+ PyTuple_SetItem(%PYARG_0, 2, %CONVERTTOPYTHON[int](a2));
+ PyTuple_SetItem(%PYARG_0, 3, %CONVERTTOPYTHON[int](a3));
</template>
<template name="fix_native_return_int*,int*,int*,int*">
PyObject* _obj = %PYARG_0;
+ Shiboken::AutoDecRef _obj0(PySequence_GetItem(_obj, 0));
+ Shiboken::AutoDecRef _obj1(PySequence_GetItem(_obj, 1));
+ Shiboken::AutoDecRef _obj2(PySequence_GetItem(_obj, 2));
+ Shiboken::AutoDecRef _obj3(PySequence_GetItem(_obj, 3));
if (!PySequence_Check(_obj)
- || PySequence_Fast_GET_SIZE(_obj) != 4
- || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 0))
- || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 1))
- || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 2))
- || !PyNumber_Check(PySequence_Fast_GET_ITEM(_obj, 3))) {
+ || PySequence_Size(_obj) != 4
+ || !PyNumber_Check(_obj0)
+ || !PyNumber_Check(_obj1)
+ || !PyNumber_Check(_obj2)
+ || !PyNumber_Check(_obj3)) {
PyErr_SetString(PyExc_TypeError, "Sequence of 4 numbers expected");
} else {
- *%1 = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(_obj, 0));
- *%2 = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(_obj, 1));
- *%3 = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(_obj, 2));
- *%4 = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(_obj, 3));
+ *%1 = %CONVERTTOCPP[int](_obj0);
+ *%2 = %CONVERTTOCPP[int](_obj1);
+ *%3 = %CONVERTTOCPP[int](_obj2);
+ *%4 = %CONVERTTOCPP[int](_obj3);
}
</template>
<modify-function signature="getMargins(int*,int*,int*,int*)const">
<modify-argument index="1">
<remove-argument/>
<conversion-rule class="native">
- const auto %out = PySequence_Size(%PYARG_1);
+ const Py_ssize_t %out = PySequence_Size(%PYARG_1);
</conversion-rule>
</modify-argument>
<modify-argument index="2">
<replace-type modified-type="PySequence"/>
<conversion-rule class="native">
- const auto numItems = PySequence_Size(%PYARG_1);
+ const Py_ssize_t numItems = PySequence_Size(%PYARG_1);
Shiboken::AutoArrayPointer<int> %out(numItems);
for (Py_ssize_t i = 0; i < numItems; ++i) {
- if (%CHECKTYPE[int](PySequence_Fast_GET_ITEM(%PYARG_1, i)))
- %out[i] = %CONVERTTOCPP[int](PySequence_Fast_GET_ITEM(%PYARG_1, i));
- else if (%ISCONVERTIBLE[int](PySequence_Fast_GET_ITEM(%PYARG_1, i)))
+ Shiboken::AutoDecRef _obj(PySequence_GetItem(%PYARG_1, i));
+ if (%CHECKTYPE[int](_obj))
+ %out[i] = %CONVERTTOCPP[int](_obj);
+ else if (%ISCONVERTIBLE[int](_obj))
%out[i] = -1;
else
%out[i] = -2;
<replace-type modified-type="PySequence"/>
</modify-argument>
<inject-code class="target" position="beginning">
- int numItems = PySequence_Size(%PYARG_1);
+ Py_ssize_t numItems = PySequence_Size(%PYARG_1);
int *cppItems = new int[numItems];
- for (int i = 0; i < numItems; i++)
- cppItems[i] = %CONVERTTOCPP[int](PySequence_GetItem(%PYARG_1, i));
+ for (int i = 0; i < numItems; i++) {
+ Shiboken::AutoDecRef _obj(PySequence_GetItem(%PYARG_1, i));
+ cppItems[i] = %CONVERTTOCPP[int](_obj);
+ }
%RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(numItems, cppItems);
%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
delete[] cppItems;
</object-type>
<value-type name="Point">
- <add-function signature="__str__" return-type="PyObject*">
+ <add-function signature="__str__" return-type="str">
<inject-code class="target" position="beginning">
int x1 = (int) %CPPSELF.x();
int x2 = ((int) (%CPPSELF.x() * 100)) - (x1 * 100);
%PYARG_0 = Shiboken::String::fromFormat("Point(%d.%d, %d.%d)", x1, x2, y1, y2);
</inject-code>
</add-function>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
int x1 = (int) %CPPSELF.x();
int x2 = ((int) (%CPPSELF.x() * 10)) - (x1 * 10);
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
PyObject* type = PyObject_Type(%PYSELF);
PyObject* args = NULL;
</value-type>
<value-type name="PointF">
- <add-function signature="__str__" return-type="PyObject*">
+ <add-function signature="__str__" return-type="str">
<inject-code class="target" position="beginning">
int x1 = (int) %CPPSELF.x();
int x2 = ((int) (%CPPSELF.x() * 100)) - (x1 * 100);
%PYARG_0 = Shiboken::String::fromFormat("PointF(%d.%d, %d.%d)", x1, x2, y1, y2);
</inject-code>
</add-function>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
int x1 = (int) %CPPSELF.x();
int x2 = ((int) (%CPPSELF.x() * 10)) - (x1 * 10);
</inject-code>
</add-function>
- <add-function signature="__reduce__" return-type="PyObject*">
+ <add-function signature="__reduce__" return-type="str">
<inject-code class="target" position="beginning">
PyObject *type = PyObject_Type(%PYSELF);
PyObject *args = NULL;
<conversion-rule class="native">
{
Shiboken::AutoDecRef strList(PySequence_Fast(%PYARG_1, "The argument must be a sequence."));
- int lineCount = PySequence_Fast_GET_SIZE(strList.object());
+ const Py_ssize_t lineCount = PySequence_Size(strList.object());
for (int line = 0; line < lineCount; ++line) {
- if (!Shiboken::String::check(PySequence_Fast_GET_ITEM(strList.object(), line))) {
+ Shiboken::AutoDecRef _obj(PySequence_GetItem(strList.object(), line));
+ bool isString = Shiboken::String::check(_obj);
+ if (!isString) {
PyErr_SetString(PyExc_TypeError, "The argument must be a sequence of strings.");
break;
}
</value-type>
<value-type name="Str" hash-function="strHash">
- <add-function signature="__str__" return-type="PyObject*">
+ <add-function signature="__str__" return-type="str">
<inject-code class="target" position="beginning">
%PYARG_0 = Shiboken::String::fromCString(%CPPSELF.cstring());
</inject-code>
</add-function>
- <add-function signature="__len__" >
+ <add-function signature="__len__" return-type="int">
<inject-code class="target" position="end">
return %CPPSELF.size();
</inject-code>
Py_INCREF(data);
}
- %0 = new %TYPE(PyBytes_AsString(data), PyBytes_GET_SIZE(data));
+ %0 = new %TYPE(PyBytes_AsString(data), PyBytes_Size(data));
Py_DECREF(data);
</inject-code>
</modify-function>
Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1));
if (!data.isNull()) {
ByteArray ba(*%CPPSELF);
- ba.append(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object()));
+ ba.append(PyBytes_AsString(data.object()), PyBytes_Size(data.object()));
%PYARG_0 = %CONVERTTOPYTHON[ByteArray](ba);
}
</inject-code>
<inject-code>
Shiboken::AutoDecRef data(PyUnicode_AsASCIIString(%PYARG_1));
if (!data.isNull()) {
- ByteArray ba(PyBytes_AsString(data.object()), PyBytes_GET_SIZE(data.object()));
+ ByteArray ba(PyBytes_AsString(data.object()), PyBytes_Size(data.object()));
ba.append(*%CPPSELF);
%PYARG_0 = %CONVERTTOPYTHON[ByteArray](ba);
}
</add-function>
<add-function signature="operator+(PyBytes,ByteArray)">
<inject-code>
- ByteArray ba(PyBytes_AsString(%PYARG_1), PyBytes_GET_SIZE(%PYARG_1));
+ ByteArray ba(PyBytes_AsString(%PYARG_1), PyBytes_Size(%PYARG_1));
ba = ba + *%CPPSELF;
%PYARG_0 = %CONVERTTOPYTHON[ByteArray](ba);
</inject-code>
</add-function>
<add-function signature="operator+(PyBytes)">
<inject-code>
- ByteArray ba(PyBytes_AsString(%PYARG_1), PyBytes_GET_SIZE(%PYARG_1));
+ ByteArray ba(PyBytes_AsString(%PYARG_1), PyBytes_Size(%PYARG_1));
ba.append(*%CPPSELF);
%PYARG_0 = %CONVERTTOPYTHON[ByteArray](ba);
</inject-code>
</add-function>
- <add-function signature="__repr__" return-type="PyObject*">
+ <add-function signature="__repr__" return-type="str">
<inject-code class="target" position="beginning">
ByteArray b(Py_TYPE(%PYSELF)->tp_name);
PyObject* aux = Shiboken::String::fromStringAndSize(%CPPSELF.data(), %CPPSELF.size());
aux = tmp;
}
b += "('";
- b += ByteArray(PyBytes_AS_STRING(aux), PyBytes_GET_SIZE(aux));
+ b += ByteArray(PyBytes_AsString(aux), PyBytes_Size(aux));
b += "')";
%PYARG_0 = Shiboken::String::fromStringAndSize(b.data(), b.size());
</inject-code>
</inject-code>
</add-function>
- <add-function signature="__len__">
+ <add-function signature="__len__" return-type="int">
<inject-code class="target" position="beginning">
return %CPPSELF.size();
</inject-code>
<value-type name="StrList">
<enum-type name="CtorEnum"/>
- <add-function signature="__len__" >
+ <add-function signature="__len__" return-type="int">
<inject-code class="target" position="end">
return %CPPSELF.size();
</inject-code>
<add-function signature="countVarargs(int, ...)" return-type="int">
<inject-code class="target" position="beginning">
- %RETURN_TYPE %0 = PyTuple_GET_SIZE(%PYARG_2);
+ %RETURN_TYPE %0 = PyTuple_Size(%PYARG_2);
%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
</inject-code>
</add-function>
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for a class that holds a void pointer.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for ...'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for user added constructors'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for virtual destructor.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test cases for virtual methods.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test case for a class that holds a void pointer.'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
'''Test weakref support'''
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
look_for = Path("testing")
here = Path(__file__).resolve().parent
while here / look_for not in here.iterdir():
- import pprint
parent = here.parent
if parent == here:
raise SystemError(look_for + " not found!")
def add_lib_dirs(lib_dirs):
"""Add directories to the platform's library path."""
if sys.platform == 'win32':
- if sys.version_info >= (3, 8, 0):
- for lib_dir in lib_dirs:
- os.add_dll_directory(lib_dir)
- else:
- _prepend_path_var('PATH', lib_dirs)
+ for lib_dir in lib_dirs:
+ os.add_dll_directory(lib_dir)
else:
_prepend_path_var('LD_LIBRARY_PATH', lib_dirs)
shiboken_dir = Path(get_build_dir()) / 'shiboken6'
lib_dirs = [os.fspath(shiboken_dir / 'libshiboken')]
if include_shiboken_tests:
- shiboken_test_dir = shiboken_dir /'tests'
+ shiboken_test_dir = shiboken_dir / 'tests'
for module in ['minimal', 'sample', 'smart', 'other']:
module_dir = shiboken_test_dir / f"{module}binding"
python_dirs.append(os.fspath(module_dir))
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
def objectFullname(t):
- # '__qualname__' for Python 2 does exist for PySide types, only.
name = getattr(t, "__qualname__", t.__name__)
module = t.__module__
if module is None or module == str.__class__.__module__:
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
${CMAKE_CURRENT_BINARY_DIR}/smart/sharedptr_integer2_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/stdsharedptrtestbench_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/stdsharedptrvirtualmethodtester_wrapper.cpp
+${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_double_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_integer_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_int_wrapper.cpp
${CMAKE_CURRENT_BINARY_DIR}/smart/std_shared_ptr_std_string_wrapper.cpp
"${CMAKE_CURRENT_BINARY_DIR}/smart-binding.txt" @ONLY)
shiboken_get_tool_shell_wrapper(shiboken tool_wrapper)
+if(SHIBOKEN_UNOPTIMIZE)
+ SET(UNOPTIMIZE "--unoptimize=${SHIBOKEN_UNOPTIMIZE}")
+ENDIF()
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/mjb_rejected_classes.log"
${tool_wrapper}
$<TARGET_FILE:Shiboken6::shiboken6>
--project-file=${CMAKE_CURRENT_BINARY_DIR}/smart-binding.txt
+ ${UNOPTIMIZE}
${GENERATOR_EXTRA_FLAGS}
DEPENDS ${smart_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h Shiboken6::shiboken6
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import gc
import os
self.assertEqual(objCount(), 10)
# clear and delete all objects in the list
- del ptrToObjList[:] # Python 2.7 lists have no clear method
+ ptrToObjList.clear()
# PYSIDE-535: Need to collect garbage in PyPy to trigger deletion
gc.collect()
self.assertEqual(len(ptrToObjList), 0)
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
ci = std.optional_int(43)
self.assertEqual(ci.value(), 43)
+ @unittest.skipIf(True, """PYSIDE-2854, T &std::optional::value() does not work/
+ returns self (colocated).""")
def testInteger(self):
b = StdOptionalTestBench()
i = b.optionalInteger()
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
from shiboken_paths import init_paths
init_paths()
-from smart import Integer, StdSharedPtrTestBench, StdSharedPtrVirtualMethodTester, std
+from smart import Integer, StdDoublePtr, StdSharedPtrTestBench, StdSharedPtrVirtualMethodTester, std
def call_func_on_ptr(ptr):
ip = std.StdIntPtr(42)
StdSharedPtrTestBench.printInt(ip)
+ def testDouble(self):
+ np = StdSharedPtrTestBench.createNullDouble()
+ StdSharedPtrTestBench.printDouble(np)
+ self.assertFalse(np)
+ p = StdSharedPtrTestBench.createDouble(67)
+ StdSharedPtrTestBench.printDouble(p)
+ dp = StdDoublePtr(42)
+ StdSharedPtrTestBench.printDouble(dp)
+
def testString(self):
np = StdSharedPtrTestBench.createNullString()
StdSharedPtrTestBench.printString(np)
#!/usr/bin/env python
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import os
import sys
</template>
<template name="pyseq_to_cpplist_convertion">
Shiboken::AutoDecRef seq(PySequence_Fast(%in, 0));
- for (int i = 0, size = PySequence_Fast_GET_SIZE(seq.object()); i < size; ++i) {
- PyObject* pyItem = PySequence_Fast_GET_ITEM(seq.object(), i);
+ for (Py_ssize_t i = 0, size = PySequence_Size(seq.object()); i < size; ++i) {
+ Shiboken::AutoDecRef pyItem(PySequence_GetItem(seq.object(), i));
%OUTTYPE_0 cppItem = %CONVERTTOCPP[%OUTTYPE_0](pyItem);
%out.push_back(cppItem);
}
<value-type name="StdOptionalTestBench"/>
<system-include file-name="memory"/>
+ <system-include file-name="optional"/>
<namespace-type name="std">
<include file-name="memory" location="global"/>
value-check-method="operator bool"
ref-count-method="use_count"
reset-method="reset"
- instantiations="Integer,int=StdIntPtr,std::string">
+ instantiations="Integer,int=StdIntPtr,double=::StdDoublePtr,std::string">
<include file-name="memory" location="global"/>
</smart-pointer-type>
declare_test(dummygentest)
add_dependencies(dummygenerator generatorrunner)
-
debug = sparse
no-suppress-warnings
-
QTEST_APPLESS_MAIN(DummyGenTest)
#include "dummygentest.moc"
-
#endif
#endif // DUMMYGENTESTCONFIG_H
-
args.append(argv[i]);
return QProcess::execute("generatorrunner", args);
}
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
testing/__init__.py
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
testing/blacklist.py
from .helper import decorate
-class BlackList(object):
+class BlackList:
def __init__(self, blname):
if not blname:
f = StringIO()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
testing/buildlog.py
is_ci = os.environ.get("QTEST_ENVIRONMENT", "") == "ci"
-class BuildLog(object):
+class BuildLog:
"""
This class is a convenience wrapper around a list of log entries.
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
testrunner
def main():
+ global COIN_THRESHOLD
# create the top-level command parser
start_time = timer()
all_projects = "shiboken6 pyside6".split()
tested_projects = "shiboken6 pyside6".split()
tested_projects_quoted = " ".join("'i'" for i in tested_projects)
+ runs = COIN_TESTING
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description=dedent(
- """\
+ f"""\
Run the tests for some projects, default = {tested_projects_quoted}.
Testing is now repeated up to {COIN_TESTING} times, and errors are
type=int,
help="use build number n (0-based), latest = -1 (default)",
)
+ parser_test.add_argument("--reruns", "-r", default=COIN_TESTING, type=int,
+ help=f"Number of re-runs (defaults to {COIN_TESTING})")
parser_test.add_argument(
"--projects",
nargs="+",
print(builds.selected.build_dir, "written to file", args.filename.name)
sys.exit(0)
elif args.subparser_name == "test":
- pass # we do it afterwards
+ runs = args.reruns
+ if runs < COIN_TESTING:
+ COIN_THRESHOLD = 1
elif args.subparser_name == "list":
rp = os.path.relpath
print()
q = 5 * [0]
- runs = COIN_TESTING
fail_crit = COIN_THRESHOLD
# now loop over the projects and accumulate
fatal = False
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
testing/helper.py
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import os
import re
return result
-class TestParser(object):
+class TestParser:
def __init__(self, test_log):
self._results = _parse_tests(test_log)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import inspect
import os
from build_scripts.utils import detect_clang
-class TestRunner(object):
+class TestRunner:
def __init__(self, log_entry, project, index):
self.log_entry = log_entry
built_path = log_entry.build_dir
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
This script is used by Coin (coin_test_instructions.py specifically) to
suffix = "exe" if sys.platform == "win32" else "bin"
+ # fetch app->title from the config
+ app_title = parser.get("app", "title")
if sys.platform != "darwin":
- binary = f"{tmpdirname}/{main_file.stem}.{suffix}"
+ binary = f"{tmpdirname}/{app_title}.{suffix}"
else:
binary = f"{tmpdirname}/pyside_app_demo.app/Contents/MacOS/{main_file.stem}"
generate_build_cmake()
run_ninja()
- if sys.version_info[:2] >= (3, 7):
- log.info("Checking Python Interface Files in Python 3 with all features selected")
- with tempfile.TemporaryDirectory() as tmpdirname:
- src_path = Path(tmpdirname) / "pyi_test"
- pyi_script_dir = Path(setup_script_dir) / "sources" / "pyside6" / "PySide6" / "support"
- execute_script(
- pyi_script_dir / "generate_pyi.py",
- "all",
- "--outpath",
- src_path,
- "--feature",
- "snake_case",
- "true_property",
- )
- from PySide6 import __all__ as modules
-
- for modname in modules:
- # PYSIDE-1735: pyi files are no longer compatible with Python.
- # XXX Maybe add a test with Mypy here?
- pass # execute_script(src_path / f"{modname}.pyi")
+ log.info("Checking Python Interface Files in Python 3 with all features selected")
+ with tempfile.TemporaryDirectory() as tmpdirname:
+ src_path = Path(tmpdirname) / "pyi_test"
+ pyi_script_dir = Path(setup_script_dir) / "sources" / "pyside6" / "PySide6" / "support"
+ execute_script(
+ pyi_script_dir / "generate_pyi.py",
+ "all",
+ "--outpath",
+ src_path,
+ "--feature",
+ "snake_case",
+ "true_property",
+ )
+ from PySide6 import __all__ as modules
+
+ for modname in modules:
+ # PYSIDE-1735: pyi files are no longer compatible with Python.
+ # XXX Maybe add a test with Mypy here?
+ pass # execute_script(src_path / f"{modname}.pyi")
def run_wheel_tests(install_wheels, wheels_dir_name):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
testrunner.py
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+# broken_links.py
+# This script is used to find the broken links in the generated sphinx documentation.
+
+# Pre-requisites before running this script:
+#
+# 1. Generate the HTML documentation as given in
+# https://doc.qt.io/qtforpython-6/gettingstarted/index.html#building-the-documentation
+# . This will generate a folder inside 'pyside-setup' called 'html'.
+# 2. Run sphinx-build with linkcheck builder to generate an output.json file inside 'html' folder.
+# The command to run is:
+# sphinx-build -b linkcheck -j auto -n -c ./html/pyside6/base <folder_of_rst> ./html
+#
+# The final output will be <output_folder>/broken_links.json
+
+import json
+import argparse
+from pathlib import Path
+
+
+def find_broken_links(file_path):
+ with open(file_path, 'r') as file:
+ lines = file.readlines()
+
+ broken_links = []
+ for line in lines:
+ entry = json.loads(line)
+ if entry.get("status") == "broken":
+ broken_links.append(entry)
+
+ return broken_links
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description="Find broken links in a JSON file.")
+ parser.add_argument('--input-file', type=str, required=True, help='Path to the input JSON file')
+ parser.add_argument('--output-folder', type=str, default=Path(__file__).resolve().parents[2],
+ help='Path to the output folder')
+ args = parser.parse_args()
+
+ broken_links = find_broken_links(args.input_file)
+
+ output_file_path = Path(args.output_folder) / 'broken_links.json'
+
+ if broken_links:
+ with open(output_file_path, 'w') as outfile:
+ json.dump(broken_links, outfile, indent=4)
+ print(f"Broken links written to {output_file_path}")
+ else:
+ print("No broken links found.")
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
#!/usr/bin/env python
#
dependency.dump()
print("\n")
-
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import re
import os
from argparse import ArgumentParser, Namespace, RawTextHelpFormatter
from pathlib import Path
from subprocess import PIPE, Popen, check_output
-from typing import Dict, List, Tuple
content_header = """Qt for Python @VERSION is a @TYPE release.
return out.decode("utf-8")
-def git_get_sha1s(versions: List[str], pattern: str):
+def git_get_sha1s(versions: list[str], pattern: str):
"""Return a list of SHA1s matching a pattern"""
command = "git rev-list --reverse --grep '^{}'".format(pattern)
command += " {}..{}".format(versions[0], versions[1])
return [s.decode("utf-8") for s in out_sha1.splitlines() if s not in pick_to_sha1]
-def git_command(versions: List[str], pattern: str):
+def git_command(versions: list[str], pattern: str):
task_number_re = re.compile(r'^.*-(\d+)\s*$')
for sha in git_get_sha1s(versions, pattern):
content = get_commit_content(sha).splitlines()
task_number_match = task_number_re.match(task)
if task_number_match:
task_number = int(task_number_match.group(1))
- entry = {"title": title, "task": task, "task-number": task_number}
+ entry = {"title": title, "task": task, "task-number": str(task_number)}
if "shiboken" in title:
if sha not in shiboken6_commits:
shiboken6_commits[sha] = entry
pyside6_commits[sha] = entry
-def create_fixes_log(versions: List[str]) -> None:
+def create_fixes_log(versions: list[str]) -> None:
git_command(versions, "Fixes: ")
-def create_task_log(versions: List[str]) -> None:
+def create_task_log(versions: list[str]) -> None:
git_command(versions, "Task-number: ")
-def extract_change_log(commit_message: List[str]) -> Tuple[str, int, str]:
+def extract_change_log(commit_message: list[str]) -> tuple[str, int, str]:
"""Extract a tuple of (component, task-number, change log paragraph)
from a commit message of the form [ChangeLog][shiboken6] description..."""
result = ''
return (component, task_nr_int, format_text(result))
-def create_change_log(versions: List[str]) -> None:
+def create_change_log(versions: list[str]) -> None:
for sha in git_get_sha1s(versions, r"\[ChangeLog\]"):
change_log = extract_change_log(get_commit_content(sha).splitlines())
component, task_nr, text = change_log
pyside6_changelogs.append((task_nr, text))
-def format_commit_msg(entry: Dict[str, str]) -> str:
+def format_commit_msg(entry: dict[str, str]) -> str:
task = entry["task"].replace("Fixes: ", "").replace("Task-number: ", "")
title = entry["title"]
if title.startswith("shiboken6: "):
return format_text(f"[{task}] {title}")
-def gen_list(d: Dict[str, Dict[str, str]]) -> str:
+def gen_list(d: dict[str, dict[str, str]]) -> str:
return "\n".join(format_commit_msg(v)
for _, v in d.items())
-def sort_dict(d: Dict[str, Dict[str, str]]) -> Dict[str, Dict[str, str]]:
- return dict(sorted(d.items(), key=lambda kv: kv[1]['task-number']))
+def sort_dict(d: dict[str, dict[str, str]]) -> dict[str, dict[str, str]]:
+ return dict(sorted(d.items(), key=lambda kv: int(kv[1]['task-number'])))
-def sort_changelog(c: List[Tuple[int, str]]) -> List[Tuple[int, str]]:
+def sort_changelog(c: list[tuple[int, str]]) -> list[tuple[int, str]]:
return sorted(c, key=lambda task_text_tuple: task_text_tuple[0])
if __name__ == "__main__":
args = parse_options()
- pyside6_commits: Dict[str, Dict[str, str]] = {}
- shiboken6_commits: Dict[str, Dict[str, str]] = {}
+ pyside6_commits: dict[str, dict[str, str]] = {}
+ shiboken6_commits: dict[str, dict[str, str]] = {}
# Changelogs are tuples of task number/formatted text
- pyside6_changelogs: List[Tuple[int, str]] = []
- shiboken6_changelogs: List[Tuple[int, str]] = []
+ pyside6_changelogs: list[tuple[int, str]] = []
+ shiboken6_changelogs: list[tuple[int, str]] = []
exclude_pick_to = args.exclude
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import logging
import shutil
+import re
import os
import stat
import sys
from urllib import request
from pathlib import Path
-from typing import List
from packaging import version
from tqdm import tqdm
# the tag number does not matter much since we update the sdk later
DEFAULT_SDK_TAG = 6514223
ANDROID_NDK_VERSION = "26b"
+ANDROID_NDK_VERSION_NUMBER_SUFFIX = "10909125"
-def run_command(command: List[str], cwd: str = None, ignore_fail: bool = False,
+def run_command(command: list[str], cwd: str | None = None, ignore_fail: bool = False,
dry_run: bool = False, accept_prompts: bool = False, show_stdout: bool = False,
capture_stdout: bool = False):
accept_prompts=accept_license, show_stdout=show_stdout)
-def _unpack(zip_file: Path, destination: Path):
+def extract_zip(file: Path, destination: Path):
"""
- Unpacks the zip_file into destination preserving all permissions
+ Unpacks the zip file into destination preserving all permissions
TODO: Try to use zipfile module. Currently we cannot use zipfile module here because
extractAll() does not preserve permissions.
raise RuntimeError("Unable to find program unzip. Use `sudo apt-get install unzip`"
"to install it")
- command = [unzip, zip_file, "-d", destination]
+ command = [unzip, str(file), "-d", str(destination)]
run_command(command=command, show_stdout=True)
+def extract_dmg(file: Path, destination: Path):
+ output = run_command(['hdiutil', 'attach', '-nobrowse', '-readonly', str(file)],
+ show_stdout=True, capture_stdout=True)
+
+ # find the mounted volume
+ result = re.search(r'/Volumes/(.*)', output)
+ if not result:
+ raise RuntimeError(f"Unable to find mounted volume for file {file}")
+ mounted_vol_name = result.group(1)
+ if not mounted_vol_name:
+ raise RuntimeError(f"Unable to find mounted volume for file {file}")
+
+ # copy files
+ shutil.copytree(f'/Volumes/{mounted_vol_name}/', destination, dirs_exist_ok=True)
+
+ # Detach mounted volume
+ run_command(['hdiutil', 'detach', f'/Volumes/{mounted_vol_name}'])
+
+
def _download(url: str, destination: Path):
"""
Download url to destination
with DownloadProgressBar(unit='B', unit_scale=True, miniters=1, desc=url.split('/')[-1]) as t:
download_path, headers = request.urlretrieve(url=url, filename=destination,
reporthook=t.update_to)
- assert headers["Content-Type"] == "application/zip"
assert Path(download_path).resolve() == destination
Downloads the given ndk_version into ndk_path
"""
ndk_path = ndk_path / "android-ndk"
- ndk_zip_path = ndk_path / f"android-ndk-r{ANDROID_NDK_VERSION}-linux.zip"
- ndk_version_path = ndk_path / f"android-ndk-r{ANDROID_NDK_VERSION}"
+ ndk_extension = "dmg" if sys.platform == "darwin" else "zip"
+ ndk_zip_path = ndk_path / f"android-ndk-r{ANDROID_NDK_VERSION}-{sys.platform}.{ndk_extension}"
+ ndk_version_path = ""
+ if sys.platform == "linux":
+ ndk_version_path = ndk_path / f"android-ndk-r{ANDROID_NDK_VERSION}"
+ elif sys.platform == "darwin":
+ ndk_version_path = (ndk_path
+ / f"AndroidNDK{ANDROID_NDK_VERSION_NUMBER_SUFFIX}.app/Contents/NDK")
+ else:
+ raise RuntimeError(f"Unsupported platform {sys.platform}")
if ndk_version_path.exists():
print(f"NDK path found in {str(ndk_version_path)}")
else:
- ndk_path.mkdir(parents=True, exist_ok=True)
- url = (f"https://dl.google.com/android/repository"
- f"/android-ndk-r{ANDROID_NDK_VERSION}-linux.zip")
-
- print(f"Downloading Android Ndk version r{ANDROID_NDK_VERSION}")
- _download(url=url, destination=ndk_zip_path)
-
- print("Unpacking Android Ndk")
- _unpack(zip_file=(ndk_path / f"android-ndk-r{ANDROID_NDK_VERSION}-linux.zip"),
- destination=ndk_path)
+ try:
+ ndk_path.mkdir(parents=True, exist_ok=True)
+ url = (f"https://dl.google.com/android/repository"
+ f"/android-ndk-r{ANDROID_NDK_VERSION}-{sys.platform}.{ndk_extension}")
+
+ print(f"Downloading Android Ndk version r{ANDROID_NDK_VERSION}")
+ _download(url=url, destination=ndk_zip_path)
+
+ print("Unpacking Android Ndk")
+ if sys.platform == "darwin":
+ extract_dmg(file=(ndk_path
+ / f"android-ndk-r{ANDROID_NDK_VERSION}-{sys.platform}.{ndk_extension}"),
+ destination=ndk_path)
+ ndk_version_path = (ndk_version_path
+ / (f"AndroidNDK{ANDROID_NDK_VERSION_NUMBER_SUFFIX}.app"
+ "/Contents/NDK"))
+ else:
+ extract_zip(file=(ndk_path
+ / f"android-ndk-r{ANDROID_NDK_VERSION}-{sys.platform}.{ndk_extension}"),
+ destination=ndk_path)
+ except Exception as e:
+ print(f"Error occurred while downloading and unpacking Android NDK: {e}")
+ if ndk_path.exists():
+ shutil.rmtree(ndk_path)
+ sys.exit(1)
return ndk_version_path
"""
Downloads Android commandline tools into cltools_path.
"""
+ sdk_platform = sys.platform if sys.platform != "darwin" else "mac"
android_sdk_dir = android_sdk_dir / "android-sdk"
url = ("https://dl.google.com/android/repository/"
- f"commandlinetools-linux-{DEFAULT_SDK_TAG}_latest.zip")
- cltools_zip_path = android_sdk_dir / f"commandlinetools-linux-{DEFAULT_SDK_TAG}_latest.zip"
+ f"commandlinetools-{sdk_platform}-{DEFAULT_SDK_TAG}_latest.zip")
+ cltools_zip_path = (android_sdk_dir
+ / f"commandlinetools-{sdk_platform}-{DEFAULT_SDK_TAG}_latest.zip")
cltools_path = android_sdk_dir / "tools"
if cltools_path.exists():
print(f"Command-line tools found in {str(cltools_path)}")
else:
- android_sdk_dir.mkdir(parents=True, exist_ok=True)
+ try:
+ android_sdk_dir.mkdir(parents=True, exist_ok=True)
- print("Download Android Command Line Tools: "
- f"commandlinetools-linux-{DEFAULT_SDK_TAG}_latest.zip")
- _download(url=url, destination=cltools_zip_path)
+ print("Download Android Command Line Tools: "
+ f"commandlinetools-{sys.platform}-{DEFAULT_SDK_TAG}_latest.zip")
+ _download(url=url, destination=cltools_zip_path)
- print("Unpacking Android Command Line Tools")
- _unpack(zip_file=cltools_zip_path, destination=android_sdk_dir)
+ print("Unpacking Android Command Line Tools")
+ extract_zip(file=cltools_zip_path, destination=android_sdk_dir)
+ except Exception as e:
+ print(f"Error occurred while downloading and unpacking Android Command Line Tools: {e}")
+ if android_sdk_dir.exists():
+ shutil.rmtree(android_sdk_dir)
+ sys.exit(1)
return android_sdk_dir
if not available_build_tools_v:
raise RuntimeError('Unable to find any build tools available for download')
+ # find the latest build tools version that is not a release candidate
+ # release candidates end has rc in the version number
+ available_build_tools_v = [v for v in available_build_tools_v if "rc" not in str(v)]
+
return max(available_build_tools_v)
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import sys
import logging
from dataclasses import dataclass
from pathlib import Path
-from git import Repo, RemoteProgress
+from git import Repo, RemoteProgress, GitCommandError
from tqdm import tqdm
from jinja2 import Environment, FileSystemLoader
download_android_ndk, install_android_packages)
# Note: Does not work with PyEnv. Your Host Python should contain openssl.
+# also update the version in ShibokenHelpers.cmake if Python version changes.
PYTHON_VERSION = "3.11"
+# minimum Android API version support. This is set according to Qt's requiremnts and needs to
+# be updated if Qt's minimum API level is updated.
+MIN_ANDROID_API_LEVEL = "28"
+
SKIP_UPDATE_HELP = ("skip the updation of SDK packages build-tools, platform-tools to"
" latest version")
If --clean-cache is used and no explicit value is suppied, then `all` is used as default.
''')
+COIN_RUN_HELP = ('''
+When run by Qt's continuos integration system COIN. This option is irrelevant to user building
+their own wheels.
+''')
+
@dataclass
class PlatformData:
return "-occp" in sys.argv or "--only-cross-compile-python" in sys.argv
+def download_only_exists():
+ '''
+ check if '--download-only' exists in command line arguments
+ '''
+ return "--download-only" in sys.argv
+
+
class CloneProgress(RemoteProgress):
def __init__(self):
super().__init__()
parser.add_argument("-v", "--verbose", help="run in verbose mode", action="store_const",
dest="loglevel", const=logging.INFO)
- parser.add_argument("--api-level", type=str, default="33", help="Android API level to use")
- parser.add_argument("--ndk-path", type=str, help="Path to Android NDK (Preferred r25c)")
+ parser.add_argument("--api-level", type=str, default="34",
+ help="Minimum Android API level to use")
+ parser.add_argument("--ndk-path", type=str, help="Path to Android NDK (Preferred r26b)")
# sdk path is needed to compile all the Qt Java Acitivity files into Qt6AndroidBindings.jar
parser.add_argument("--sdk-path", type=str, help="Path to Android SDK")
- parser.add_argument("--qt-install-path", type=str, required=not occp_exists(),
- help="Qt installation path eg: /home/Qt/6.5.0")
+ parser.add_argument(
+ "--qt-install-path",
+ type=str,
+ required=not (occp_exists() or download_only_exists()),
+ help="Qt installation path eg: /home/Qt/6.8.0"
+ )
parser.add_argument("-occp", "--only-cross-compile-python", action="store_true",
help="Only cross compiles Python for the specified Android platform")
choices=["all", "python", "ndk", "sdk", "toolchain"],
help=CLEAN_CACHE_HELP)
+ parser.add_argument("--coin", action="store_true",
+ help=COIN_RUN_HELP)
+
+ parser.add_argument("--download-only", action="store_true",
+ help="Only download Android NDK and SDK")
+
args = parser.parse_args()
logging.basicConfig(level=args.loglevel)
skip_update = args.skip_update
auto_accept_license = args.auto_accept_license
clean_cache = args.clean_cache
+ coin = args.coin
+ download_only = args.download_only
# auto download Android NDK and SDK
pyside6_deploy_cache = Path.home() / ".pyside6_android_deploy"
# download and unzip command-line tools
sdk_path = download_android_commandlinetools(pyside6_deploy_cache)
# install and update required android packages
- install_android_packages(android_sdk_dir=sdk_path, android_api=api_level, dry_run=dry_run,
- accept_license=auto_accept_license, skip_update=skip_update)
+ install_android_packages(android_sdk_dir=sdk_path, android_api=api_level,
+ dry_run=dry_run, accept_license=auto_accept_license,
+ skip_update=skip_update)
+
+ if download_only:
+ print(f"Android NDK and SDK downloaded successfully into {pyside6_deploy_cache}")
+ sys.exit(0)
templates_path = Path(__file__).parent / "templates"
platform_data = PlatformData("x86_64", api_level, "x86_64", "x86_64", "x86-64", "64")
# python path is valid, if Python for android installation exists in python_path
- python_path = (pyside6_deploy_cache / f"Python-{platform_data.plat_name}-linux-android"
- / "_install")
+ python_path = (pyside6_deploy_cache
+ / f"Python-{platform_data.plat_name}-linux-android" / "_install")
valid_python_path = python_path.exists()
if Path(python_path).exists():
expected_dirs = ["lib", "include"]
if not cpython_dir.exists():
logging.info(f"cloning cpython {PYTHON_VERSION}")
- Repo.clone_from(
- "https://github.com/python/cpython.git",
- cpython_dir,
- progress=CloneProgress(),
- branch=PYTHON_VERSION,
- )
+ try:
+ Repo.clone_from(
+ "https://github.com/python/cpython.git",
+ cpython_dir,
+ progress=CloneProgress(),
+ branch=PYTHON_VERSION,
+ )
+ except GitCommandError as e:
+ # Print detailed error information
+ print(f"Error cloning repository: {e}")
+ print(f"Command: {e.command}")
+ print(f"Status: {e.status}")
+ print(f"Stderr: {e.stderr}")
if not python_ccompile_script.exists():
+ host_system_config_name = run_command("./config.guess", cwd=cpython_dir,
+ dry_run=dry_run, show_stdout=True,
+ capture_stdout=True).strip()
+
# use jinja2 to create cross_compile.sh script
template = environment.get_template("cross_compile.tmpl.sh")
content = template.render(
ndk_path=ndk_path,
api_level=platform_data.api_level,
android_py_install_path_prefix=pyside6_deploy_cache,
- host_python_path=sys.executable
+ host_python_path=sys.executable,
+ python_version=PYTHON_VERSION,
+ host_system_name=host_system_config_name,
+ host_platform_name=sys.platform
)
logging.info(f"Writing Python cross compile script into {python_ccompile_script}")
run_command([f"./{python_ccompile_script.name}"], cwd=cpython_dir, dry_run=dry_run,
show_stdout=True)
- # run patchelf to change the SONAME of libpython from libpython3.x.so.1.0 to
- # libpython3.x.so, to match with python_for_android's Python library. Otherwise,
- # the Qfp binaries won't be able to link to Python
- run_command(["patchelf", "--set-soname", f"libpython{PYTHON_VERSION}.so",
- f"libpython{PYTHON_VERSION}.so.1.0"], cwd=Path(python_path) / "lib",
- dry_run=dry_run)
-
logging.info(
f"Cross compile Python for Android platform {platform_data.plat_name}. "
f"Final installation in {python_path}"
gcc_march=platform_data.gcc_march,
plat_bits=platform_data.plat_bits,
python_version=PYTHON_VERSION,
- target_python_path=python_path
+ target_python_path=python_path,
+ min_android_api=MIN_ANDROID_API_LEVEL
)
logging.info(f"Writing Qt for Python toolchain file into {qfp_toolchain}")
# give run permission to cross compile script
qfp_toolchain.chmod(qfp_toolchain.stat().st_mode | stat.S_IEXEC)
+ if sys.platform == "linux":
+ host_qt_install_suffix = "gcc_64"
+ elif sys.platform == "darwin":
+ host_qt_install_suffix = "macos"
+ else:
+ raise RuntimeError("Qt for Python cross compilation not supported on this platform")
+
+ if coin:
+ target_path = str(Path(qt_install_path) / "target")
+ qt_host_install_path = qt_install_path
+ else:
+ target_path = str(Path(qt_install_path) / f"android_{platform_data.qt_plat_name}")
+ qt_host_install_path = str(Path(qt_install_path) / host_qt_install_suffix)
+
# run the cross compile script
logging.info(f"Running Qt for Python cross-compile for platform {platform_data.plat_name}")
qfp_ccompile_cmd = [sys.executable, "setup.py", "bdist_wheel", "--parallel=9",
"--standalone",
f"--cmake-toolchain-file={str(qfp_toolchain.resolve())}",
- f"--qt-host-path={qt_install_path}/gcc_64",
+ f"--qt-host-path={qt_host_install_path}",
f"--plat-name=android_{platform_data.plat_name}",
f"--python-target-path={python_path}",
- (f"--qt-target-path={qt_install_path}/"
- f"android_{platform_data.qt_plat_name}"),
+ f"--qt-target-path={target_path}",
"--no-qt-tools"]
run_command(qfp_ccompile_cmd, cwd=pyside_setup_dir, dry_run=dry_run, show_stdout=True)
gitpython
+# Fixes: ImportError: cannot import name 'soft_unicode' from 'markupsafe' caused when jinja2 is
+# installed
+markupsafe==2.0.1
Jinja2
tqdm
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
set -x -e
export HOST_ARCH={{ plat_name }}-linux-android
-export TOOLCHAIN={{ ndk_path }}/toolchains/llvm/prebuilt/linux-x86_64/bin
+export TOOLCHAIN={{ ndk_path }}/toolchains/llvm/prebuilt/{{ host_platform_name }}-x86_64/bin
export TOOL_PREFIX=$TOOLCHAIN/$HOST_ARCH
export PLATFORM_API={{ api_level }}
{% if plat_name == "armv7a" -%}
export LD=$TOOLCHAIN/ld
export READELF=$TOOLCHAIN/llvm-readelf
export CFLAGS='-fPIC -DANDROID'
-./configure --host=$HOST_ARCH --target=$HOST_ARCH --build=x86_64-pc-linux-gnu \
+./configure --host=$HOST_ARCH --target=$HOST_ARCH --build={{ host_system_name }} \
--with-build-python={{ host_python_path }} --enable-shared \
--enable-ipv6 ac_cv_file__dev_ptmx=yes ac_cv_file__dev_ptc=no --without-ensurepip \
ac_cv_little_endian_double=yes
-make BLDSHARED="$CC -shared" CROSS-COMPILE=$TOOL_PREFIX- CROSS_COMPILE_TARGET=yes
-make install BLDSHARED="$CC -shared" CROSS-COMPILE=$TOOL_PREFIX- \
-CROSS_COMPILE_TARGET=yes prefix={{ android_py_install_path_prefix }}/Python-$HOST_ARCH/_install
+make BLDSHARED="$CC -shared" CROSS-COMPILE=$TOOL_PREFIX- CROSS_COMPILE_TARGET=yes \
+INSTSONAME=libpython{{ python_version }}.so
+make install prefix={{ android_py_install_path_prefix }}/Python-$HOST_ARCH/_install
set(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION clang)
set(CMAKE_ANDROID_STL_TYPE c++_shared)
if(NOT DEFINED ANDROID_PLATFORM AND NOT DEFINED ANDROID_NATIVE_API_LEVEL)
- set(ANDROID_PLATFORM "android-{{ api_level }}" CACHE STRING "")
+ set(ANDROID_PLATFORM "android-{{ min_android_api }}" CACHE STRING "")
endif()
set(ANDROID_SDK_ROOT {{ sdk_path }})
{% if plat_name == "armv7a" -%}
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
import re
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import argparse
import ctypes
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import os
import subprocess
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""Helper functions for formatting information on QMetaObject"""
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
from enum import IntEnum, Enum
from pathlib import Path
from textwrap import dedent
+from collections import defaultdict
class Format(Enum):
BASE_CONTENT = """\
+.. _pyside6_examples:
+
Examples
========
"""
+def tutorial_headline(path: str):
+ if "tutorials/extending-qml/chapter" in path:
+ return "Tutorial: Writing QML Extensions with Python"
+ if "tutorials/extending-qml-advanced/advanced" in path:
+ return "Tutorial: Writing advanced QML Extensions with Python"
+ if "tutorials/finance_manager" in path:
+ return "Tutorial: Finance Manager - Integrating PySide6 with SQLAlchemy and FastAPI"
+ return ""
+
+
def ind(x):
return " " * 4 * x
has_doc: bool
img_doc: Path
headline: str
+ tutorial: str
def get_module_gallery(examples):
"""
gallery = (
- ".. grid:: 1 4 4 4\n"
- f"{ind(1)}:gutter: 2\n\n"
+ f"{ind(1)}.. grid:: 1 3 3 3\n"
+ f"{ind(2)}:gutter: 3\n\n"
)
# Iteration per rows
for i in range(math.ceil(len(examples))):
e = examples[i]
suffix = SUFFIXES[e.file_format]
- url = e.doc_file.replace(f".{suffix}", ".html")
+ # doc_file with suffix removed, to be used as a sphinx reference
+ doc_file_name = e.doc_file.replace(f".{suffix}", "")
+ # lower case sphinx reference
+ # this seems to be a bug or a requirement from sphinx
+ doc_file_name = doc_file_name.lower()
+
name = e.example
underline = e.module
elif name.startswith("advanced"):
name = name.replace("advanced", "a")
- desc = e.headline
- if not desc:
- desc = f"found in the ``{underline}`` directory."
+ # Handling description from original file
+ desc = ""
+ original_dir = Path(e.abs_path) / "doc"
+
+ if e.has_doc:
+ # cannot use e.doc_file because that is the target file name
+ # so finding the original file by the name
+ original_file = (next(original_dir.glob("*.rst"), None)
+ or next(original_dir.glob("*.md"), None))
+ if not original_file:
+ # ideally won't reach here because has_doc is True
+ print(f"example_gallery: No .rst or .md file found in {original_dir}")
+ continue
+
+ with original_file.open("r") as f:
+ # Read the first line
+ first_line = f.readline().strip()
+
+ # Check if the first line is a reference (starts with '(' and ends with ')=' for MD,
+ # or starts with '.. ' and ends with '::' for RST)
+ if ((e.file_format == Format.MD and first_line.startswith('(')
+ and first_line.endswith(')='))
+ or (e.file_format == Format.RST and first_line.startswith('.. ')
+ and first_line.endswith('::'))):
+ # The first line is a reference, so read the next lines until a non-empty line
+ # is found
+ while True:
+ title_line = f.readline().strip()
+ if title_line:
+ break
+ else:
+ # The first line is the title
+ title_line = first_line
+
+ # The next line handling depends on the file format
+ line = f.readline().strip()
+
+ if e.file_format == Format.MD:
+ # For markdown, the second line is the empty line
+ if line != "":
+ # If the line is not empty, raise a runtime error
+ raise RuntimeError(f"Unexpected line: {line} in {original_file}. "
+ "Needs handling.")
+ else:
+ # For RST and other formats
+ # The second line is the underline under the title
+ _ = line
+ # The next line should be empty
+ line = f.readline().strip()
+ if line != "":
+ raise RuntimeError(f"Unexpected line: {line} in {original_file}. "
+ "Needs handling.")
+
+ # Now read until another empty line
+ lines = []
+ while True:
+ line = f.readline().strip()
+ if line.startswith(".. tags") or line.startswith("#"):
+ # Skip the empty line
+ _ = f.readline()
+ # Read the next line
+ line = f.readline().strip()
+
+ if not line:
+ break
+ lines.append(line)
+
+ desc = " ".join(lines)
+ if len(desc) > 120:
+ desc = desc[:120] + "..."
+ else:
+ print(f"example_gallery: No .rst or .md file found in {original_dir}")
+
+ title = e.headline
+ if not title:
+ title = f"{name} from ``{underline}``."
+
+ # Clean refs from desc
+ if ":ref:" in desc:
+ desc = desc.replace(":ref:`", "")
+ desc = desc.replace("`", "")
- gallery += f"{ind(1)}.. grid-item-card:: {name}\n"
- gallery += f"{ind(2)}:class-item: cover-img\n"
- gallery += f"{ind(2)}:link: {url}\n"
- gallery += f"{ind(2)}:img-top: {img_name}\n\n"
- gallery += f"{ind(2)}{desc}\n"
+ gallery += f"{ind(2)}.. grid-item-card:: {title}\n"
+ gallery += f"{ind(3)}:class-item: cover-img\n"
+ gallery += f"{ind(3)}:link: {doc_file_name}\n"
+ gallery += f"{ind(3)}:link-type: ref\n"
+ gallery += f"{ind(3)}:img-top: {img_name}\n\n"
+ gallery += f"{ind(3)}+++\n{ind(3)}{desc}\n"
return f"{gallery}\n"
return "\n".join(new_s)
-def make_zip_archive(zip_name, src, skip_dirs=None):
+def make_zip_archive(zip_file, src, skip_dirs=None):
src_path = Path(src).expanduser().resolve(strict=True)
if skip_dirs is None:
skip_dirs = []
if not isinstance(skip_dirs, list):
print("Error: A list needs to be passed for 'skip_dirs'")
return
- with zipfile.ZipFile(src_path.parents[0] / Path(zip_name), 'w', zipfile.ZIP_DEFLATED) as zf:
+ with zipfile.ZipFile(zip_file, 'w', zipfile.ZIP_DEFLATED) as zf:
for file in src_path.rglob('*'):
skip = False
_parts = file.relative_to(src_path).parts
content = "\n"
# Prepare ZIP file, and copy to final destination
- zip_name = f"{project_dir.name}.zip"
- make_zip_archive(zip_name, project_dir, skip_dirs=["doc"])
- zip_src = f"{project_dir}.zip"
- zip_dst = EXAMPLES_DOC / zip_name
- shutil.move(zip_src, zip_dst)
+ # Handle examples which only have a dummy pyproject file in the "doc" dir
+ zip_root = project_dir.parent if project_dir.name == "doc" else project_dir
+ zip_name = f"{zip_root.name}.zip"
+ make_zip_archive(EXAMPLES_DOC / zip_name, zip_root, skip_dirs=["doc"])
if file_format == Format.RST:
content += f":download:`Download this example <{zip_name}>`\n\n"
p = ExampleParameters()
p.example_dir = pyproject_file.parent
+ if list(p.example_dir.parent.glob("*.qmlproject")) and p.example_dir.name == "Python":
+ # Design Studio project example
+ p.example_dir = pyproject_file.parent.parent
+
if p.example_dir.name == "doc": # Dummy pyproject in doc dir (scriptableapplication)
p.example_dir = p.example_dir.parent
result.abs_path = str(p.example_dir)
result.has_doc = bool(p.src_doc_file_path)
result.img_doc = p.src_screenshot
+ result.tutorial = tutorial_headline(result.abs_path)
files = []
try:
headline = ""
if files:
doc_file = EXAMPLES_DOC / p.target_doc_file
+ sphnx_ref_example = p.target_doc_file.replace(f'.{SUFFIXES[p.file_format]}', '')
+ # lower case sphinx reference
+ # this seems to be a bug or a requirement from sphinx
+ sphnx_ref_example = sphnx_ref_example.lower()
+ content_f = ""
+ if p.file_format == Format.RST:
+ content_f = f".. _{sphnx_ref_example}:\n\n"
+ elif p.file_format == Format.MD:
+ content_f = f"({sphnx_ref_example})=\n\n"
+ else:
+ print(f"example_gallery: Invalid file format {p.file_format}", file=sys.stderr)
+ raise ValueError
+
with open(doc_file, "w", encoding="utf-8") as out_f:
if p.src_doc_file_path:
- content_f = read_rst_file(p.example_dir, files, p.src_doc_file_path)
+ content_f += read_rst_file(p.example_dir, files, p.src_doc_file_path)
headline = get_headline(content_f, p.file_format)
if not headline:
print(f"example_gallery: No headline found in {doc_file}",
resources.append(p.src_screenshot)
write_resources(resources, EXAMPLES_DOC)
else:
- content_f = get_header_title(p.example_dir)
+ content_f += get_header_title(p.example_dir)
content_f += get_code_tabs(files, pyproject_file.parent, p.file_format)
out_f.write(content_f)
def example_sort_key(example: ExampleData):
- name = example.example
- return "AAA" + name if "gallery" in name else name
+ result = ""
+ if example.tutorial:
+ result += "AA:" + example.tutorial + ":"
+ elif "gallery" in example.example:
+ result += "AB:"
+ result += example.example
+ return result
def sort_examples(example):
f.write(BASE_CONTENT)
for module_name in sorted(examples.keys(), key=module_sort_key):
e = examples.get(module_name)
- for i in e:
- index_files.append(i.doc_file)
+ tutorial_examples = defaultdict(list)
+ non_tutorial_examples = []
+
+ for example in e:
+ index_files.append(example.doc_file)
+ if example.tutorial:
+ tutorial_examples[example.tutorial].append(example)
+ else:
+ non_tutorial_examples.append(example)
+
title = module_title(module_name)
- f.write(f"{title}\n")
- f.write(f"{'*' * len(title)}\n")
- f.write(get_module_gallery(e))
+ f.write(f".. dropdown:: {title}\n\n")
+
+ # Write tutorial examples under their tutorial names
+ for tutorial_name, tutorial_exs in tutorial_examples.items():
+ f.write(f"{ind(1)}**{tutorial_name}**\n\n")
+ f.write(get_module_gallery(tutorial_exs))
+
+ # If there are non-tutorial examples and tutorials exist
+ if tutorial_examples and non_tutorial_examples:
+ f.write(f"{ind(1)}**Other Examples**\n\n")
+ f.write(get_module_gallery(non_tutorial_examples))
+ # If no tutorials exist, list all examples
+ elif not tutorial_examples:
+ f.write(get_module_gallery(e))
+
f.write("\n\n")
f.write(footer_index)
for i in index_files:
--- /dev/null
+#!/bin/bash
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+if [ ! -d "/Users/qt/python311/bin" ]; then
+ cd /Users/qt/work
+ curl -O https://www.python.org/ftp/python/3.11.9/Python-3.11.9.tar.xz
+ if [ $? -ne 0 ]; then
+ echo "Failed to download Python source code."
+ exit 1
+ fi
+
+ tar xJf Python-3.11.9.tar.xz
+ if [ $? -ne 0 ]; then
+ echo "Failed to extract Python source code."
+ exit 1
+ fi
+
+ cd Python-3.11.9/
+ ./configure --prefix=/Users/qt/python311 --with-openssl=/usr/local/opt/openssl --enable-optimizations
+ if [ $? -ne 0 ]; then
+ echo "Failed to configure Python."
+ exit 1
+ fi
+ make
+ if [ $? -ne 0 ]; then
+ echo "Failed to compile Python."
+ exit 1
+ fi
+ make install
+ if [ $? -ne 0 ]; then
+ echo "Failed to install Python."
+ exit 1
+ fi
+fi
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+from __future__ import annotations
"""
leak_finder.py
have_debug = False
-class LeakFinder(object):
+class LeakFinder:
def __init__(self):
self.all, self.refs = self._make_snapshot()
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import os
import subprocess
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import sys
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
modules_to_test = {
# 6.0
'QtQuickWidgets': 'qtquickwidgets-module.html',
# Broken in 6.5.0
#'QtQuickControls2': 'qtquickcontrols-module.html',
+ 'QtQuickControls2': 'qtquickcontrols2-module.html',
'QtSql': 'qtsql-module.html',
'QtWidgets': 'qtwidgets-module.html',
'QtConcurrent': 'qtconcurrent-module.html',
'QtSvgWidgets': 'qtsvgwidgets-module.html',
'QtUiTools': 'qtuitools-module.html',
'QtXml': 'qtxml-module.html',
+ 'QtQuickTest': 'qtquicktest-module.html',
'QtTest': 'qttest-module.html',
- 'Qt3DCore': 'qt3dcore-module.html',
- 'Qt3DInput': 'qt3dinput-module.html',
- 'Qt3DLogic': 'qt3dlogic-module.html',
- 'Qt3DRender': 'qt3drender-module.html',
- 'Qt3DAnimation': 'qt3danimation-module.html',
- 'Qt3DExtras': 'qt3dextras-module.html',
- 'QtNetworkAuth': 'qtnetworkauth-module.html',
+ # Deprecated in 6.8
+ #'Qt3DCore': 'qt3dcore-module.html',
+ #'Qt3DInput': 'qt3dinput-module.html',
+ #'Qt3DLogic': 'qt3dlogic-module.html',
+ #'Qt3DRender': 'qt3drender-module.html',
+ #'Qt3DAnimation': 'qt3danimation-module.html',
+ #'Qt3DExtras': 'qt3dextras-module.html',
+ 'QtNetworkAuth': 'qtnetworkauth-module.html',
'QtStateMachine': 'qtstatemachine-module.html',
- # 'QtCoAp' -- TODO
- # 'QtMqtt' -- TODO
- # 'QtOpcUA' -- TODO
+ 'QtCoap': 'qtcoap-module.html',
+ 'QtMqtt': 'qtmqtt-module.html',
+ 'QtOpcUa': 'qtopcua-module.html',
+ 'QtVirtualKeyboard': 'qtvirtualkeyboard-module.html',
+ 'QtWaylandCompositor': 'qtwaylandcompositor-module.html',
+ 'QtWebView': 'qtwebview-module.html',
# 6.1
- 'QtScxml': 'qtscxml-module.html',
- 'QtCharts': 'qtcharts-module.html',
- 'QtDataVisualization': 'qtdatavisualization-module.html',
+ 'QtScxml': 'qtscxml-module.html',
# 6.2
'QtBluetooth': 'qtbluetooth-module.html',
- 'QtPositioning': 'qtpositioning-module.html',
- 'QtMultimedia': 'qtmultimedia-module.html',
- 'QtRemoteObjects': 'qtremoteobjects-module.html',
- 'QtSensors': 'qtsensors-module.html',
- 'QtSerialPort': 'qtserialport-module.html',
- 'QtWebChannel': 'qtwebchannel-module.html',
- 'QtWebEngineCore': 'qtwebenginecore-module.html',
- 'QtWebEngineQuick': 'qtwebenginequick-module.html',
- 'QtWebEngineWidgets': 'qtwebenginewidgets-module.html',
- 'QtWebSockets': 'qtwebsockets-module.html',
+ 'QtPositioning': 'qtpositioning-module.html',
+ 'QtMultimedia': 'qtmultimedia-module.html',
+ 'QtRemoteObjects': 'qtremoteobjects-module.html',
+ 'QtSensors': 'qtsensors-module.html',
+ 'QtSerialPort': 'qtserialport-module.html',
+ 'QtWebChannel': 'qtwebchannel-module.html',
+ 'QtWebEngineCore': 'qtwebenginecore-module.html',
+ 'QtWebEngineQuick': 'qtwebenginequick-module.html',
+ 'QtWebEngineWidgets': 'qtwebenginewidgets-module.html',
+ 'QtWebSockets': 'qtwebsockets-module.html',
'QtHttpServer': 'qthttpserver-module.html',
# 6.3
#'QtSpeech': 'qtspeech-module.html',
- 'QtMultimediaWidgets': 'qtmultimediawidgets-module.html',
+ 'QtMultimediaWidgets': 'qtmultimediawidgets-module.html',
'QtNfc': 'qtnfc-module.html',
'QtQuick3D': 'qtquick3d-module.html',
# 6.4
- 'QtPdf': 'qtpdf-module.html', # this include qtpdfwidgets
+ 'QtPdf': 'qtpdf-module.html', # this include qtpdfwidgets
'QtSpatialAudio': 'qtspatialaudio-module.html',
# 6.5
- 'QtSerialBus': 'qtserialbus-module.html',
+ 'QtSerialBus': 'qtserialbus-module.html',
'QtTextToSpeech': 'qttexttospeech-module.html',
- 'QtLocation': 'qtlocation-module.html',
+ 'QtLocation': 'qtlocation-module.html',
+ 'QtGRPC': 'qtgrpc-module.html',
+ 'QtPhotobuf': 'qtprotobuf-module.html',
+ # 6.6
+ 'QtGraphs': 'qtgraphs-module.html',
+
+ # 6.7
+ 'QtCharts': 'qtcharts-module-qtcharts.html',
+ 'QtDataVisualization': 'qtdatavisualization-module-qtdatavis.html',
}
types_to_ignore = {
'QSqlDriverCreator',
'QSqlDriverPlugin',
}
+
+# Split modules on PySide
+split_modules = {
+ 'QtSvg': 'QtSvgWidgets',
+ 'QtPdf': 'QtPdfWidgets',
+ 'QtOpenGL': 'QtOpenGLWidgets',
+ 'QtGraphs': 'QtGraphsWidgets',
+}
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
# This script is used to generate a summary of missing types / classes
# which are present in C++ Qt6, but are missing in PySide6.
from pathlib import Path
from bs4 import BeautifulSoup
-from config import modules_to_test, types_to_ignore
+from config import modules_to_test, types_to_ignore, split_modules
import pandas as pd
import matplotlib.pyplot as plt
qt_documentation_website_prefixes = {
- "6.5": "https://doc.qt.io/qt-6/",
+ "6.7": "https://doc.qt.io/qt-6/",
"dev": "https://doc-snapshots.qt.io/qt6-dev/",
}
parser.add_argument(
"--qt-version",
"-v",
- default="6.5",
- choices=["6.5", "dev"],
+ default="6.7",
+ choices=["6.7", "dev"],
type=str,
dest="version",
help="the Qt version to use to check for types",
style="end",
)
- wikilog(
- "Similar report:\n https://gist.github.com/ethanhs/6c626ca4e291f3682589699296377d3a",
- style="text_with_link",
- )
+ # wikilog(
+ # "Similar report:\n https://gist.github.com/ethanhs/6c626ca4e291f3682589699296377d3a",
+ # style="text_with_link",
+ # )
python_executable = Path(sys.executable).name or ""
command_line_arguments = " ".join(sys.argv)
pyside_tested_module = getattr(
__import__(pyside_package_name, fromlist=[module_name]), module_name
)
+ for main_module, split_module in split_modules.items():
+ # If module name matches with the split_modules
+ # Second module is also imported
+ if module_name == main_module:
+ pyside_tested_module_split = getattr(
+ __import__(pyside_package_name, fromlist=[split_module]), split_module
+ )
except Exception as e:
e_str = str(e).replace('"', "")
wikilog(
pyqt_module_name = module_name
pyqt_tested_module = getattr(
- __import__(pyqt_package_name, fromlist=[pyqt_module_name]), pyqt_module_name
+ __import__(pyqt_package_name, fromlist=[module_name]), pyqt_module_name
)
except Exception as e:
e_str = str(e).replace("'", "")
pyside_qualified_type = f"pyside_tested_module.{qt_type}"
eval(pyside_qualified_type)
is_present_in_pyside = True
- except Exception as e:
- print("Failed eval-in pyside qualified types")
- print(f"{type(e).__name__}: {e}")
- missing_type = qt_type
- missing_pyside_types_count += 1
- total_missing_types_count += 1
-
- if is_present_in_pyqt:
- missing_type = f"{missing_type} (is present in PyQt6)"
- missing_types_compared_to_pyqt += 1
- total_missing_types_count_compared_to_pyqt += 1
+ except Exception:
+ # If an exception is thrown in the first eval
+ # Second imported module will be checked
+ try:
+ pyside_qualified_type_split = f"pyside_tested_module_split.{qt_type}"
+ eval(pyside_qualified_type_split)
+ is_present_in_pyside = True
+ except Exception as e:
+ print("Failed eval-in pyside qualified types")
+ print(f"{type(e).__name__}: {e}")
+ missing_type = qt_type
+ missing_pyside_types_count += 1
+ total_missing_types_count += 1
+
+ if is_present_in_pyqt:
+ missing_type = f"{missing_type} (is present in PyQt6)"
+ missing_types_compared_to_pyqt += 1
+ total_missing_types_count_compared_to_pyqt += 1
# missing in PySide
if not is_present_in_pyside:
plt.xticks(rotation=45)
plt.ylabel("Types Count")
figure = plt.gcf()
- figure.set_size_inches(32, 18) # set to full_screen
+ figure.set_size_inches(32, 18) # set to full_screen
plt.savefig("missing_bindings_comparison_plot.png", bbox_inches='tight')
print(f"Plot saved in {Path.cwd() / 'missing_bindings_comparison_plot.png'}\n")
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import logging
import os
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
regenerate_example_resources.py
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
"""
regenerate_example_ui.py
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+"""
+This tool reads all the changelogs in doc/changelogs and generates .rst files for each of the
+changelogs. This .rst files are then used to generate the contents of the 'Release Notes' section
+in the navigation pane of the Qt for Python documentation.
+"""
+
+import re
+import logging
+import shutil
+from pathlib import Path
+from argparse import ArgumentParser, RawTextHelpFormatter
+
+SECTION_NAMES = ["PySide6", "Shiboken6", "PySide2", "Shiboken2"]
+DIR = Path(__file__).parent
+DEFAULT_OUTPUT_DIR = Path(f"{DIR}/../../sources/pyside6/doc/release_notes").resolve()
+CHANGELOG_DIR = Path(f"{DIR}/../../doc/changelogs").resolve()
+
+BASE_CONTENT = """\
+.. _release_notes:
+
+Release Notes
+=============
+
+This section contains the release notes for different versions of Qt for Python.
+
+.. toctree::
+ :maxdepth: 1
+
+ pyside6_release_notes.md
+ shiboken6_release_notes.md
+ pyside2_release_notes.md
+ shiboken2_release_notes.md
+"""
+
+
+class Changelog:
+ def __init__(self, file_path: Path):
+ self.file_path = file_path
+ self.version = file_path.name.split("-")[-1]
+ self.sections = {section: [] for section in SECTION_NAMES}
+ # for matching lines like * PySide6 * to identify the section
+ self.section_pattern = re.compile(r"\* +(\w+) +\*")
+ # for line that start with ' -' which lists the changes
+ self.line_pattern = re.compile(r"^ -")
+ # for line that contains a bug report like PYSIDE-<bug_number>
+ self.bug_number_pattern = re.compile(r"\[PYSIDE-\d+\]")
+
+ def add_line(self, section, line):
+ self.sections[section].append(line)
+
+ def parsed_sections(self):
+ return self.sections
+
+ def parse(self):
+ current_section = None
+ buffer = []
+
+ with open(self.file_path, 'r', encoding='utf-8') as file:
+ # convert the lines to an iterator for skip the '***' lines
+ lines = iter(file.readlines())
+
+ for line in lines:
+ # skip lines with all characters as '*'
+ if line.strip() == '*' * len(line.strip()):
+ continue
+
+ match = self.section_pattern.match(line)
+ if match:
+ # if buffer has content, add it to the current section
+ if buffer:
+ self.add_line(current_section, ' '.join(buffer).strip())
+ buffer = []
+ current_section = match.group(1)
+ # skip the next line which contains '***'
+ try:
+ next(lines)
+ except StopIteration:
+ break
+ continue
+
+ if current_section:
+ if self.line_pattern.match(line) and buffer:
+ self.add_line(current_section, ' '.join(buffer).strip())
+ buffer = []
+
+ # If the line contains a reference to a bug report like [PYSIDE-<bug_number>]
+ # then insert a link to the reference that conforms with Sphinx syntax
+ bug_number = self.bug_number_pattern.search(line)
+ if bug_number:
+ bug_number = bug_number.group()
+ # remove the square brackets
+ actual_bug_number = bug_number[1:-1]
+ bug_number_replacement = (
+ f"[{actual_bug_number}]"
+ f"(https://bugreports.qt.io/browse/{actual_bug_number})"
+ )
+ line = re.sub(re.escape(bug_number), bug_number_replacement, line)
+
+ # Add the line to the buffer
+ buffer.append(line.strip())
+
+ # Add any remaining content in the buffer to the current section
+ if buffer:
+ self.add_line(current_section, ' '.join(buffer).strip())
+
+
+def parse_changelogs() -> str:
+ '''
+ Parse the changelogs in the CHANGELOG_DIR and return a list of parsed changelogs.
+ '''
+ changelogs = []
+ logging.info(f"[RELEASE_DOC] Processing changelogs in {CHANGELOG_DIR}")
+ for file_path in CHANGELOG_DIR.iterdir():
+ # exclude changes-1.2.3
+ if "changes-1.2.3" in file_path.name:
+ continue
+ logging.info(f"[RELEASE_DOC] Processing file {file_path.name}")
+ changelog = Changelog(file_path)
+ changelog.parse()
+ changelogs.append(changelog)
+ return changelogs
+
+
+def write_md_file(section: str, changelogs: list[Changelog], output_dir: Path):
+ '''
+ For each section create a .md file with the following content:
+
+ Section Name
+ ============
+
+ Version
+ -------
+
+ - Change 1
+ - Change 2
+ ....
+ '''
+ file_path = output_dir / f"{section.lower()}_release_notes.md"
+ with open(file_path, 'w', encoding='utf-8') as file:
+ file.write(f"# {section}\n")
+ for changelog in changelogs:
+ section_contents = changelog.parsed_sections()[section]
+ if section_contents:
+ file.write(f"## {changelog.version}\n\n")
+ for lines in section_contents:
+ # separate each line with a newline
+ file.write(f"{lines}\n")
+ file.write("\n")
+
+
+def generate_index_file(output_dir: Path):
+ """Generate the index RST file."""
+ index_path = output_dir / "index.rst"
+ index_path.write_text(BASE_CONTENT, encoding='utf-8')
+
+
+def main():
+ parser = ArgumentParser(description="Generate release notes from changelog",
+ formatter_class=RawTextHelpFormatter)
+ parser.add_argument("-v", "--verbose", help="run in verbose mode", action="store_const",
+ dest="loglevel", const=logging.INFO)
+ parser.add_argument("--target", "-t", help="Directory to output the generated files",
+ type=Path, default=DEFAULT_OUTPUT_DIR)
+ args = parser.parse_args()
+
+ logging.basicConfig(level=args.loglevel)
+
+ output_dir = args.target.resolve()
+
+ # create the output directory if it does not exist
+ # otherwise remove its contents
+ if output_dir.is_dir():
+ shutil.rmtree(output_dir, ignore_errors=True)
+ logging.info(f"[RELEASE_DOC] Removed existing {output_dir}")
+
+ logging.info(f"[RELEASE_DOC] Creating {output_dir}")
+ output_dir.mkdir(exist_ok=True)
+
+ logging.info("[RELEASE_DOC] Generating index.md file")
+ generate_index_file(output_dir)
+
+ logging.info("[RELEASE_DOC] Parsing changelogs")
+ changelogs = parse_changelogs()
+
+ # sort changelogs by version number in descending order
+ changelogs.sort(key=lambda x: x.version, reverse=True)
+
+ for section in SECTION_NAMES:
+ logging.info(f"[RELEASE_DOC] Generating {section.lower()}_release_notes.md file")
+ write_md_file(section, changelogs, output_dir)
+
+
+if __name__ == "__main__":
+ main()
--- /dev/null
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
+
+from pathlib import Path
+import os
+import re
+import subprocess
+import sys
+
+"""Scan the Qt C++ headers per module for classes that should be present
+ in the matching type system and print the missing classes."""
+
+
+VALUE_TYPE = re.compile(r'^\s*<value-type name="([^"]+)"')
+
+
+OBJECT_TYPE = re.compile(r'^\s*<object-type name="([^"]+)"')
+
+
+def query_qtpaths(keyword):
+ """Query qtpaths for a keyword."""
+ query_cmd = ["qtpaths", "-query", keyword]
+ output = subprocess.check_output(query_cmd, stderr=subprocess.STDOUT,
+ universal_newlines=True)
+ return output.strip()
+
+
+def is_class_exluded(name):
+ """Check for excluded classes that do not make sense in a typesystem."""
+ if len(name) < 2:
+ return True
+ if "Iterator" in name or "iterator" in name:
+ return True
+ if name.startswith("If") or name.startswith("Is") or name.startswith("When"):
+ return True
+ if name[:1].islower():
+ return True
+ if name.startswith("QOpenGLFunctions") and name.endswith("Backend"):
+ return True
+ return False
+
+
+def class_from_header_line(line):
+ """Extract a class name from a C++ header line."""
+ def _is_macro(token):
+ return "EXPORT" in token or "API" in token
+
+ def _fix_class_name(name):
+ pos = name.find('<') # Some template specialization "class Name<TemplateParam>"
+ if pos > 0:
+ name = name[:pos]
+ if name.endswith(':'):
+ name = name[:-1]
+ return name
+
+ if line.startswith('//') or line.endswith(';'): # comment/forward decl
+ return None
+ line = line.strip()
+ if not line.startswith("class ") and not line.startswith("struct "):
+ return None
+ tokens = line.split()
+ pos = 1
+ while pos < len(tokens) and _is_macro(tokens[pos]):
+ pos += 1
+ return _fix_class_name(tokens[pos]) if pos < len(tokens) else None
+
+
+def classes_from_header(header):
+ """Extract classes from C++ header file."""
+ result = []
+ for line in header.read_text("utf-8").splitlines():
+ name = class_from_header_line(line)
+ if name and not is_class_exluded(name):
+ result.append(name)
+ return sorted(result)
+
+
+def classes_from_typesystem(typesystem):
+ """Extract classes from typesystem XML file."""
+ result = []
+ for line in typesystem.read_text("utf-8").splitlines():
+ match = VALUE_TYPE.search(line) or OBJECT_TYPE.search(line)
+ if match:
+ result.append(match.group(1))
+ return sorted(result)
+
+
+def check_classes(qt_module_inc_dir, pyside_dir):
+ """Check classes of a module."""
+ module_name = qt_module_inc_dir.name
+ sys.stderr.write(f"Checking {module_name} ")
+ cpp_classes = []
+ typesystem_classes = []
+ for header in qt_module_inc_dir.glob("q*.h"):
+ if not header.name.endswith("_p.h"):
+ cpp_classes.extend(classes_from_header(header))
+ for typesystem in pyside_dir.glob("*.xml"):
+ typesystem_classes.extend(classes_from_typesystem(typesystem))
+
+ cpp_count = len(cpp_classes)
+ typesystem_count = len(typesystem_classes)
+ sys.stderr.write(f"found {cpp_count} C++ / {typesystem_count} typesystem classes")
+ if cpp_count <= typesystem_count:
+ sys.stderr.write(" ok\n")
+ else:
+ sys.stderr.write(f", {cpp_count-typesystem_count} missing\n")
+ for cpp_class in cpp_classes:
+ if cpp_class not in typesystem_classes:
+ wrapper_name = cpp_class.lower() + "_wrapper.cpp"
+ print(f"{module_name}:{cpp_class}:{wrapper_name}")
+
+
+if __name__ == '__main__':
+ qt_version = query_qtpaths("QT_VERSION")
+ qt_inc_dir = Path(query_qtpaths("QT_INSTALL_HEADERS"))
+ print(f"Qt {qt_version} at {os.fspath(qt_inc_dir.parent)}", file=sys.stderr)
+
+ dir = Path(__file__).parents[1].resolve()
+ for module_dir in (dir / "sources" / "pyside6" / "PySide6").glob("Qt*"):
+ qt_module_inc_dir = qt_inc_dir / module_dir.name
+ if qt_module_inc_dir.is_dir():
+ check_classes(qt_module_inc_dir, module_dir)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import re
def snippet_translate(x):
global switch_var, switch_branch
- ## Cases which are not C++
- ## TODO: Maybe expand this with lines that doesn't need to be translated
+ # # Cases which are not C++
+ # # TODO: Maybe expand this with lines that doesn't need to be translated
if x.strip().startswith("content-type: text/html"):
return x
- ## General Rules
+ # # General Rules
# Remove ';' at the end of the lines
has_semicolon = x.endswith(";")
if "nullptr" in x:
x = x.replace("nullptr", "None")
- ## Special Cases Rules
+ # # Special Cases Rules
xs = x.strip()
# Special case for 'main'
if xs.startswith("int main("):
# with only alpha numeric content
if VAR4_PATTERN.search(xs) and not xs.endswith(")"):
v = x.rstrip()
- if (not v.endswith(" True") and not v.endswith(" False")
- and not v.endswith(" None")):
+ if (not v.endswith(" True") and not v.endswith(" False") and not v.endswith(" None")):
x = f"{v}()"
return dstrip(x)
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import re
import sys
clean_x = x.strip()
if clean_x.startswith(operator) or clean_x.endswith(operator):
x = x.replace(operator, "")
- x = f"{x} = {clean_x.replace(operator, '')} {operator[0]} 1"
+ x = f"{x} {operator[0]}= 1"
return x
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import logging
import os
from enum import Enum
from pathlib import Path
from textwrap import dedent
-from typing import Dict, List
from override import python_example_snippet_mapping
from converter import snippet_translate
return True
-def get_snippet_ids(line: str, pattern: re.Pattern) -> List[str]:
+def get_snippet_ids(line: str, pattern: re.Pattern) -> list[str]:
# Extract the snippet ids for a line '//! [1] //! [2]'
result = []
for m in pattern.finditer(line):
return result
-def overriden_snippet_lines(lines: List[str], start_id: str) -> List[str]:
+def overriden_snippet_lines(lines: list[str], start_id: str) -> list[str]:
"""Wrap an overridden snippet with marker and id lines."""
id_string = f"//! [{start_id}]"
result = [OVERRIDDEN_SNIPPET, id_string]
return result
-def get_snippet_override(start_id: str, rel_path: str) -> List[str]:
+def get_snippet_override(start_id: str, rel_path: Path) -> list[str]:
"""Check if the snippet is overridden by a local file under
sources/pyside6/doc/snippets."""
file_start_id = start_id.replace(' ', '_')
return overriden_snippet_lines(lines, start_id)
-def _get_snippets(lines: List[str],
+def _get_snippets(lines: list[str],
comment: str,
- pattern: re.Pattern) -> Dict[str, List[str]]:
+ pattern: re.Pattern) -> dict[str, list[str]]:
"""Helper to extract (potentially overlapping) snippets from a C++ file
indicated by pattern ("//! [1]") and return them as a dict by <id>."""
- snippets: Dict[str, List[str]] = {}
- snippet: List[str]
- done_snippets : List[str] = []
+ snippets: dict[str, list[str]] = {}
+ snippet: list[str]
+ done_snippets: list[str] = []
i = 0
while i < len(lines):
# Find the end of the snippet
j = i
while j < len(lines):
- l = lines[j]
+ line = lines[j]
j += 1
# Add the line to the snippet
- snippet.append(l)
+ snippet.append(line)
# Check if the snippet is complete
- if start_id in get_snippet_ids(l, pattern):
+ if start_id in get_snippet_ids(line, pattern):
# End of snippet
snippet[len(snippet) - 1] = id_line
snippets[start_id] = snippet
return snippets
-def get_python_example_snippet_override(start_id: str, rel_path: str) -> List[str]:
+def get_python_example_snippet_override(start_id: str, rel_path: Path) -> list[str]:
"""Check if the snippet is overridden by a python example snippet."""
key = (os.fspath(rel_path), start_id)
value = python_example_snippet_mapping().get(key)
return overriden_snippet_lines(lines, start_id)
-def get_snippets(lines: List[str], rel_path: str) -> List[List[str]]:
+def get_snippets(lines: list[str], rel_path: Path) -> list[list[str]]:
"""Extract (potentially overlapping) snippets from a C++ file indicated
by '//! [1]'."""
result = _get_snippets(lines, '//', CPP_SNIPPET_PATTERN)
if snippet:
result[snippet_id] = snippet
- return result.values()
+ return list(result.values())
def get_license_from_file(lines):
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
module_classes = {
"QAbstractOpenGLFunctions",
"QAbstractTextDocumentLayout",
"QAccessible",
+ "QAccessibleAnnouncementEvent",
+ "QAccessibleAttributesInterface",
"QAccessibleEditableTextInterface",
"QAccessibleEvent",
"QAccessibleInterface",
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
from pathlib import Path
/ "stringlistmodel.py")
for i in range(10):
snippet_id = str(i)
- result[(qt_path, snippet_id)] = pyside_path, snippet_id
+ result[(qt_path, snippet_id)] = pyside_path, snippet_id
qt_path = "qtbase/src/widgets/doc/snippets/qlistview-dnd/model.cpp"
pyside_path = (TUTORIAL_EXAMPLES_PATH / "modelviewprogramming"
/ "qlistview-dnd.py")
for i in range(11):
snippet_id = str(i)
- result[(qt_path, snippet_id)] = pyside_path, snippet_id
+ result[(qt_path, snippet_id)] = pyside_path, snippet_id
qt_path = "qtconnectivity/examples/bluetooth/heartrate_game/devicefinder.cpp"
pyside_path = EXAMPLES_PATH / "bluetooth" / "heartrate_game" / "devicefinder.py"
for i in range(5):
snippet_id = f"devicediscovery-{i}"
- result[(qt_path, snippet_id)] = pyside_path, snippet_id
+ result[(qt_path, snippet_id)] = pyside_path, snippet_id
qt_path = "qtconnectivity/examples/bluetooth/heartrate_game/devicehandler.cpp"
pyside_path = EXAMPLES_PATH / "bluetooth" / "heartrate_game" / "devicehandler.py"
for snippet_id in ["Connect-Signals-1", "Connect-Signals-2",
"Filter HeartRate service 2", "Find HRM characteristic",
"Reading value"]:
- result[(qt_path, snippet_id)] = pyside_path, snippet_id
+ result[(qt_path, snippet_id)] = pyside_path, snippet_id
qt_path = "qtconnectivity/examples/bluetooth/heartrate_server/main.cpp"
pyside_path = EXAMPLES_PATH / "bluetooth" / "heartrate_server" / "heartrate_server.py"
for snippet_id in ["Advertising Data", "Start Advertising", "Service Data",
"Provide Heartbeat"]:
- result[(qt_path, snippet_id)] = pyside_path, snippet_id
+ result[(qt_path, snippet_id)] = pyside_path, snippet_id
_python_example_snippet_mapping = result
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import re
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
from converter import snippet_translate as st
def multi_st(lines):
- result = [st(l) for l in lines.split("\n")]
+ result = [st(line) for line in lines.split("\n")]
return "\n".join(result)
def test_inc_dec():
- assert st("++i;") == "i = i + 1"
- assert st("i--;") == "i = i - 1"
+ assert st("++i;") == "i += 1"
+ assert st("i--;") == "i -= 1"
def test_and_or():
== "elapsed = (elapsed + QTimer(sender()).interval()) % 1000"
)
assert (
- st("a = qobject_cast<type*>(data) * 9 + static_cast<int>(42)")
+ st("a = qobject_cast<type*>(data) * 9 + static_cast<int>(42)") # noqa: E126
== "a = type(data) * 9 + int(42)"
)
def test_connects():
assert (
st("connect(button, &QPushButton::clicked, this, &MyClass::slotClicked);")
- == "button.clicked.connect(self.slotClicked)"
+ == "button.clicked.connect(self.slotClicked)"
)
assert (
st("connect(m_ui->button, &QPushButton::clicked, this, &MyClass::slotClicked);")
- == "m_ui.button.clicked.connect(self.slotClicked)"
+ == "m_ui.button.clicked.connect(self.slotClicked)"
)
assert (
st("connect(button.get(), &QPushButton::clicked, this, &MyClass::slotClicked);")
- == "button.clicked.connect(self.slotClicked)"
+ == "button.clicked.connect(self.slotClicked)"
)
assert st("for (int i = 0; i <= 10; i++)") == "for i in range(0, 11):"
assert st("for (int i = 10; i >= 0; i--)") == "for i in range(-1, 10, -1):"
- ## if contains "begin()" and "end()", do a 'for it in var'
+ # # if contains "begin()" and "end()", do a 'for it in var'
assert (
st(
"for (QHash<int, QString>::const_iterator it = hash.cbegin(),"
# assert st("QDebug operator<<(QDebug dbg, const Message &message)") == "def __str__(self):"
# TODO: Maybe play with the format?
- # assert st('m_o.append(tr("version: %1.%2").arg(a).arg(b))') == 'm_o.append(tr("version: {1}.{2}".format(a, b)'
+ # assert st('m_o.append(tr("version: %1.%2").arg(a).arg(b))')
+ # == 'm_o.append(tr("version: {1}.{2}".format(a, b)'
def test_lambdas():
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
from main import _get_snippets, get_snippet_ids, CPP_SNIPPET_PATTERN
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+from __future__ import annotations
import os
import re
from argparse import ArgumentParser, RawTextHelpFormatter
from pathlib import Path
from textwrap import dedent
-from typing import Optional, Tuple
VERSION = 6
TEMP_DIR = Path(tempfile.gettempdir())
-def get_class_name(file: Path) -> Tuple[Optional[str], Optional[str]]:
+def get_class_name(file: Path) -> tuple[str | None, str | None]:
"""Return class name and widget name of UI file."""
pattern = re.compile(r'^\s*<widget class="(\w+)" name="(\w+)"\s*>.*$')
for line in Path(file).read_text().splitlines():
failed = 0
count = len(options.files)
for i, file in enumerate(options.files):
- print(f'{i+1}/{count} {file}')
+ print(f'{i + 1}/{count} {file}')
if not test_file(file, options.uic):
failed += 1
if failed != 0:
build-backend = "setuptools.build_meta"
[project]
-name = PROJECT_NAME
+name = "PROJECT_NAME"
authors = [
{name = "Qt for Python Team", email = "pyside@qt-project.org"},
]
-description = PROJECT_DESCRIPTION
-readme = PROJECT_README
+description = "PROJECT_DESCRIPTION"
+readme = "PROJECT_README"
dynamic = ["version"]
-requires-python = ">=3.9, <3.13"
+requires-python = ">=3.9, <3.14"
keywords = ["Qt"]
license = {text = "LGPL"}
-dependencies = PROJECT_DEPENDENCIES
+dependencies = "PROJECT_DEPENDENCIES"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
"Topic :: Database",
"Topic :: Software Development",
"Topic :: Software Development :: Code Generators",
Changelog = "https://code.qt.io/cgit/pyside/pyside-setup.git/tree/doc/changelogs"
Tracker = "https://bugreports.qt.io/projects/PYSIDE"
-PROJECT_SCRIPTS
+# PROJECT_SCRIPTS
[tool.distutils.bdist_wheel]
py_limited_api = "cp39"
-plat_name = PROJECT_TAG
+plat_name = "PROJECT_TAG"
[tool.setuptools.dynamic]
-version = {attr = PROJECT_VERSION}
+version = {attr = "PROJECT_VERSION"}